/* SPDX-FileCopyrightText: 2023 Blender Authors
 *
 * SPDX-License-Identifier: GPL-2.0-or-later */

/** \file
 * \ingroup bmesh
 *
 * Primitive shapes.
 */

#include "MEM_guardedalloc.h"

#include "BLI_math_base_safe.h"
#include "BLI_math_matrix.h"
#include "BLI_math_rotation.h"
#include "BLI_math_vector.h"

#include "BKE_customdata.hh"

#include "bmesh.hh"
#include "intern/bmesh_operators_private.hh"

/* ************************ primitives ******************* */

static const float icovert[12][3] = {
    {0.0f, 0.0f, -200.0f},
    {144.72f, -105.144f, -89.443f},
    {-55.277f, -170.128, -89.443f},
    {-178.885f, 0.0f, -89.443f},
    {-55.277f, 170.128f, -89.443f},
    {144.72f, 105.144f, -89.443f},
    {55.277f, -170.128f, 89.443f},
    {-144.72f, -105.144f, 89.443f},
    {-144.72f, 105.144f, 89.443f},
    {55.277f, 170.128f, 89.443f},
    {178.885f, 0.0f, 89.443f},
    {0.0f, 0.0f, 200.0f},
};

static const short icoface[20][3] = {
    {0, 1, 2},  {1, 0, 5},   {0, 2, 3},  {0, 3, 4},  {0, 4, 5},  {1, 5, 10},  {2, 1, 6},
    {3, 2, 7},  {4, 3, 8},   {5, 4, 9},  {1, 10, 6}, {2, 6, 7},  {3, 7, 8},   {4, 8, 9},
    {5, 9, 10}, {6, 10, 11}, {7, 6, 11}, {8, 7, 11}, {9, 8, 11}, {10, 9, 11},
};

static const float icouvs[60][2] = {
    {0.181819f, 0.000000f}, {0.272728f, 0.157461f}, {0.090910f, 0.157461f}, {0.272728f, 0.157461f},
    {0.363637f, 0.000000f}, {0.454546f, 0.157461f}, {0.909091f, 0.000000f}, {1.000000f, 0.157461f},
    {0.818182f, 0.157461f}, {0.727273f, 0.000000f}, {0.818182f, 0.157461f}, {0.636364f, 0.157461f},
    {0.545455f, 0.000000f}, {0.636364f, 0.157461f}, {0.454546f, 0.157461f}, {0.272728f, 0.157461f},
    {0.454546f, 0.157461f}, {0.363637f, 0.314921f}, {0.090910f, 0.157461f}, {0.272728f, 0.157461f},
    {0.181819f, 0.314921f}, {0.818182f, 0.157461f}, {1.000000f, 0.157461f}, {0.909091f, 0.314921f},
    {0.636364f, 0.157461f}, {0.818182f, 0.157461f}, {0.727273f, 0.314921f}, {0.454546f, 0.157461f},
    {0.636364f, 0.157461f}, {0.545455f, 0.314921f}, {0.272728f, 0.157461f}, {0.363637f, 0.314921f},
    {0.181819f, 0.314921f}, {0.090910f, 0.157461f}, {0.181819f, 0.314921f}, {0.000000f, 0.314921f},
    {0.818182f, 0.157461f}, {0.909091f, 0.314921f}, {0.727273f, 0.314921f}, {0.636364f, 0.157461f},
    {0.727273f, 0.314921f}, {0.545455f, 0.314921f}, {0.454546f, 0.157461f}, {0.545455f, 0.314921f},
    {0.363637f, 0.314921f}, {0.181819f, 0.314921f}, {0.363637f, 0.314921f}, {0.272728f, 0.472382f},
    {0.000000f, 0.314921f}, {0.181819f, 0.314921f}, {0.090910f, 0.472382f}, {0.727273f, 0.314921f},
    {0.909091f, 0.314921f}, {0.818182f, 0.472382f}, {0.545455f, 0.314921f}, {0.727273f, 0.314921f},
    {0.636364f, 0.472382f}, {0.363637f, 0.314921f}, {0.545455f, 0.314921f}, {0.454546f, 0.472382f},
};

static const int monkeyo = 4;
static const int monkeynv = 271;
static const int monkeynf = 250;
static const signed char monkeyv[271][3] = {
    {-71, 21, 98},    {-63, 12, 88},    {-57, 7, 74},     {-82, -3, 79},    {-82, 4, 92},
    {-82, 17, 100},   {-92, 21, 102},   {-101, 12, 95},   {-107, 7, 83},    {-117, 31, 84},
    {-109, 31, 95},   {-96, 31, 102},   {-92, 42, 102},   {-101, 50, 95},   {-107, 56, 83},
    {-82, 66, 79},    {-82, 58, 92},    {-82, 46, 100},   {-71, 42, 98},    {-63, 50, 88},
    {-57, 56, 74},    {-47, 31, 72},    {-55, 31, 86},    {-67, 31, 97},    {-66, 31, 99},
    {-70, 43, 100},   {-82, 48, 103},   {-93, 43, 105},   {-98, 31, 105},   {-93, 20, 105},
    {-82, 31, 106},   {-82, 15, 103},   {-70, 20, 100},   {-127, 55, 95},   {-127, 45, 105},
    {-127, -87, 94},  {-127, -41, 100}, {-127, -24, 102}, {-127, -99, 92},  {-127, 52, 77},
    {-127, 73, 73},   {-127, 115, -70}, {-127, 72, -109}, {-127, 9, -106},  {-127, -49, -45},
    {-101, -24, 72},  {-87, -56, 73},   {-82, -89, 73},   {-80, -114, 68},  {-85, -121, 67},
    {-104, -124, 71}, {-127, -126, 74}, {-71, -18, 68},   {-46, -5, 69},    {-21, 19, 57},
    {-17, 55, 76},    {-36, 62, 80},    {-64, 77, 88},    {-86, 97, 94},    {-107, 92, 97},
    {-119, 63, 96},   {-106, 53, 99},   {-111, 39, 98},   {-101, 12, 95},   {-79, 2, 90},
    {-64, 8, 86},     {-47, 24, 83},    {-45, 38, 83},    {-50, 48, 85},    {-72, 56, 92},
    {-95, 60, 97},    {-127, -98, 94},  {-113, -92, 94},  {-112, -107, 91}, {-119, -113, 89},
    {-127, -114, 88}, {-127, -25, 96},  {-127, -18, 95},  {-114, -19, 95},  {-111, -29, 96},
    {-116, -37, 95},  {-76, -6, 86},    {-48, 7, 80},     {-34, 26, 77},    {-32, 48, 84},
    {-39, 53, 93},    {-71, 70, 102},   {-87, 82, 107},   {-101, 79, 109},  {-114, 55, 108},
    {-111, -13, 104}, {-100, -57, 91},  {-95, -90, 88},   {-93, -105, 85},  {-97, -117, 81},
    {-106, -119, 81}, {-127, -121, 82}, {-127, 6, 93},    {-127, 27, 98},   {-85, 61, 95},
    {-106, 18, 96},   {-110, 27, 97},   {-112, -88, 94},  {-117, -57, 96},  {-127, -57, 96},
    {-127, -42, 95},  {-115, -35, 100}, {-110, -29, 102}, {-113, -17, 100}, {-122, -16, 100},
    {-127, -26, 106}, {-121, -19, 104}, {-115, -20, 104}, {-113, -29, 106}, {-117, -32, 103},
    {-127, -37, 103}, {-94, -40, 71},   {-106, -31, 91},  {-104, -40, 91},  {-97, -32, 71},
    {-127, -112, 88}, {-121, -111, 88}, {-115, -105, 91}, {-115, -95, 93},  {-127, -100, 84},
    {-115, -96, 85},  {-115, -104, 82}, {-121, -109, 81}, {-127, -110, 81}, {-105, 28, 100},
    {-103, 20, 99},   {-84, 55, 97},    {-92, 54, 99},    {-73, 51, 99},    {-55, 45, 89},
    {-52, 37, 88},    {-53, 25, 87},    {-66, 13, 92},    {-79, 8, 95},     {-98, 14, 100},
    {-104, 38, 100},  {-100, 48, 100},  {-97, 46, 97},    {-102, 38, 97},   {-96, 16, 97},
    {-79, 11, 93},    {-68, 15, 90},    {-57, 27, 86},    {-56, 36, 86},    {-59, 43, 87},
    {-74, 50, 96},    {-91, 51, 98},    {-84, 52, 96},    {-101, 22, 96},   {-102, 29, 96},
    {-113, 59, 78},   {-102, 85, 79},   {-84, 88, 76},    {-65, 71, 71},    {-40, 58, 63},
    {-25, 52, 59},    {-28, 21, 48},    {-50, 0, 53},     {-71, -12, 60},   {-127, 115, 37},
    {-127, 126, -10}, {-127, -25, -86}, {-127, -59, 24},  {-127, -125, 59}, {-127, -103, 44},
    {-127, -73, 41},  {-127, -62, 36},  {-18, 30, 7},     {-17, 41, -6},    {-28, 34, -56},
    {-68, 56, -90},   {-33, -6, 9},     {-51, -16, -21},  {-45, -1, -55},   {-84, 7, -85},
    {-97, -45, 52},   {-104, -53, 33},  {-90, -91, 49},   {-95, -64, 50},   {-85, -117, 51},
    {-109, -97, 47},  {-111, -69, 46},  {-106, -121, 56}, {-99, -36, 55},   {-100, -29, 60},
    {-101, -22, 64},  {-100, -50, 21},  {-89, -40, -34},  {-83, -19, -69},  {-69, 111, -49},
    {-69, 119, -9},   {-69, 109, 30},   {-68, 67, 55},    {-34, 52, 43},    {-46, 58, 36},
    {-45, 90, 7},     {-25, 72, 16},    {-25, 79, -15},   {-45, 96, -25},   {-45, 87, -57},
    {-25, 69, -46},   {-48, 42, -75},   {-65, 3, -70},    {-22, 42, -26},   {-75, -22, 19},
    {-72, -25, -27},  {-13, 52, -30},   {-28, -18, -16},  {6, -13, -42},    {37, 7, -55},
    {46, 41, -54},    {31, 65, -54},    {4, 61, -40},     {3, 53, -37},     {25, 56, -50},
    {35, 37, -52},    {28, 10, -52},    {5, -5, -39},     {-21, -9, -17},   {-9, 46, -28},
    {-6, 39, -37},    {-14, -3, -27},   {6, 0, -47},      {25, 12, -57},    {31, 32, -57},
    {23, 46, -56},    {4, 44, -46},     {-19, 37, -27},   {-20, 22, -35},   {-30, 12, -35},
    {-22, 11, -35},   {-19, 2, -35},    {-23, -2, -35},   {-34, 0, -9},     {-35, -3, -22},
    {-35, 5, -24},    {-25, 26, -27},   {-13, 31, -34},   {-13, 30, -41},   {-23, -2, -41},
    {-18, 2, -41},    {-21, 10, -41},   {-29, 12, -41},   {-19, 22, -41},   {6, 42, -53},
    {25, 44, -62},    {34, 31, -63},    {28, 11, -62},    {7, 0, -54},      {-14, -2, -34},
    {-5, 37, -44},    {-13, 14, -42},   {-7, 8, -43},     {1, 16, -47},     {-4, 22, -45},
    {3, 30, -48},     {8, 24, -49},     {15, 27, -50},    {12, 35, -50},    {4, 56, -62},
    {33, 60, -70},    {48, 38, -64},    {41, 7, -68},     {6, -11, -63},    {-26, -16, -42},
    {-17, 49, -49},
};

static signed char monkeyf[250][4] = {
    {27, 4, 5, 26},       {25, 4, 5, 24},      {3, 6, 5, 4},
    {1, 6, 5, 2},         {5, 6, 7, 4},        {3, 6, 7, 2},
    {5, 8, 7, 6},         {3, 8, 7, 4},        {7, 8, 9, 6},
    {5, 8, 9, 4},         {7, 10, 9, 8},       {5, 10, 9, 6},
    {9, 10, 11, 8},       {7, 10, 11, 6},      {9, 12, 11, 10},
    {7, 12, 11, 8},       {11, 6, 13, 12},     {5, 4, 13, 12},
    {3, -2, 13, 12},      {-3, -4, 13, 12},    {-5, -10, 13, 12},
    {-11, -12, 14, 12},   {-13, -18, 14, 13},  {-19, 4, 5, 13},
    {10, 12, 4, 4},       {10, 11, 9, 9},      {8, 7, 9, 9},
    {7, 5, 6, 6},         {6, 3, 4, 4},        {5, 1, 2, 2},
    {4, -1, 0, 0},        {3, -3, -2, -2},     {22, 67, 68, 23},
    {20, 65, 66, 21},     {18, 63, 64, 19},    {16, 61, 62, 17},
    {14, 59, 60, 15},     {12, 19, 48, 57},    {18, 19, 48, 47},
    {18, 19, 48, 47},     {18, 19, 48, 47},    {18, 19, 48, 47},
    {18, 19, 48, 47},     {18, 19, 48, 47},    {18, 19, 48, 47},
    {18, 19, 48, 47},     {18, -9, -8, 47},    {18, 27, 45, 46},
    {26, 55, 43, 44},     {24, 41, 42, 54},    {22, 39, 40, 23},
    {20, 37, 38, 21},     {18, 35, 36, 19},    {16, 33, 34, 17},
    {14, 31, 32, 15},     {12, 39, 30, 13},    {11, 48, 45, 38},
    {8, 36, -19, 9},      {8, -20, 44, 47},    {42, 45, 46, 43},
    {18, 19, 40, 39},     {16, 17, 38, 37},    {14, 15, 36, 35},
    {32, 44, 43, 33},     {12, 33, 32, 42},    {19, 44, 43, 42},
    {40, 41, 42, -27},    {8, 9, 39, -28},     {15, 43, 42, 16},
    {13, 43, 42, 14},     {11, 43, 42, 12},    {9, -30, 42, 10},
    {37, 12, 38, -32},    {-33, 37, 45, 46},   {-33, 40, 41, 39},
    {38, 40, 41, 37},     {36, 40, 41, 35},    {34, 40, 41, 33},
    {36, 39, 38, 37},     {35, 40, 39, 38},    {1, 2, 14, 21},
    {1, 2, 40, 13},       {1, 2, 40, 39},      {1, 24, 12, 39},
    {-34, 36, 38, 11},    {35, 38, 36, 37},    {-37, 8, 35, 37},
    {-11, -12, -45, 40},  {-11, -12, 39, 38},  {-11, -12, 37, 36},
    {-11, -12, 35, 34},   {33, 34, 40, 41},    {33, 34, 38, 39},
    {33, 34, 36, 37},     {33, -52, 34, 35},   {33, 37, 36, 34},
    {33, 35, 34, 34},     {8, 7, 37, 36},      {-32, 7, 35, 46},
    {-34, -33, 45, 46},   {4, -33, 43, 34},    {-34, -33, 41, 42},
    {-34, -33, 39, 40},   {-34, -33, 37, 38},  {-34, -33, 35, 36},
    {-34, -33, 33, 34},   {-34, -33, 31, 32},  {-34, -4, 28, 30},
    {-5, -34, 28, 27},    {-35, -44, 36, 27},  {26, 35, 36, 45},
    {24, 25, 44, 45},     {25, 23, 44, 42},    {25, 24, 41, 40},
    {25, 24, 39, 38},     {25, 24, 37, 36},    {25, 24, 35, 34},
    {25, 24, 33, 32},     {25, 24, 31, 30},    {15, 24, 29, 38},
    {25, 24, 27, 26},     {23, 12, 37, 26},    {11, 12, 35, 36},
    {-86, -59, 36, -80},  {-60, -61, 36, 35},  {-62, -63, 36, 35},
    {-64, -65, 36, 35},   {-66, -67, 36, 35},  {-68, -69, 36, 35},
    {-70, -71, 36, 35},   {-72, -73, 36, 35},  {-74, -75, 36, 35},
    {42, 43, 53, 58},     {40, 41, 57, 56},    {38, 39, 55, 57},
    {-81, -80, 37, 56},   {-83, -82, 55, 52},  {-85, -84, 51, 49},
    {-87, -86, 48, 49},   {47, 50, 51, 48},    {46, 48, 51, 49},
    {43, 46, 49, 44},     {-92, -91, 45, 42},  {-23, 49, 50, -20},
    {-94, 40, 48, -24},   {-96, -22, 48, 49},  {-97, 48, 21, -90},
    {-100, 36, 50, 23},   {22, 49, 48, -100},  {-101, 47, 46, 22},
    {21, 45, 35, 25},     {33, 34, 44, 41},    {13, 14, 28, 24},
    {-107, 26, 30, -106}, {14, 46, 45, 15},    {14, 44, 43, -110},
    {-111, 42, 23, -110}, {6, 7, 45, 46},      {45, 44, 47, 46},
    {45, 46, 47, 48},     {47, 46, 49, 48},    {17, 49, 47, 48},
    {17, 36, 46, 48},     {35, 36, 44, 45},    {35, 36, 40, 43},
    {35, 36, 38, 39},     {-4, -3, 37, 35},    {-123, 34, 33, 1},
    {-9, -8, -7, -6},     {-10, -7, 32, -125}, {-127, -11, -126, -126},
    {-7, -6, 5, 31},      {4, 5, 33, 30},      {4, 39, 33, 32},
    {4, 35, 32, 38},      {20, 21, 39, 38},    {4, 37, 38, 5},
    {-11, -10, 36, 3},    {-11, 15, 14, 35},   {13, 16, 34, 34},
    {-13, 14, 13, 13},    {-3, 1, 30, 29},     {-3, 28, 29, 1},
    {-2, 31, 28, -1},     {12, 13, 27, 30},    {-2, 26, 12, 12},
    {35, 29, 42, 36},     {34, 35, 36, 33},    {32, 35, 36, 31},
    {30, 35, 36, 29},     {28, 35, 36, 27},    {26, 35, 36, 25},
    {34, 39, 38, 35},     {32, 39, 38, 33},    {30, 39, 38, 31},
    {28, 39, 38, 29},     {26, 39, 38, 27},    {25, 31, 32, 38},
    {-18, -17, 45, 44},   {-18, 17, 28, 44},   {-24, -20, 42, -23},
    {11, 35, 27, 14},     {25, 28, 39, 41},    {37, 41, 40, 38},
    {34, 40, 36, 35},     {32, 40, 39, 33},    {30, 39, 31, 40},
    {21, 29, 39, 22},     {-31, 37, 28, 4},    {-32, 33, 35, 36},
    {32, 33, 34, 34},     {18, 35, 36, 48},    {34, 25, 40, 35},
    {24, 25, 38, 39},     {24, 25, 36, 37},    {24, 25, 34, 35},
    {24, 25, 32, 33},     {24, 13, 41, 31},    {17, 11, 41, 35},
    {15, 16, 34, 35},     {13, 14, 34, 35},    {11, 12, 34, 35},
    {9, 10, 34, 35},      {7, 8, 34, 35},      {26, 25, 37, 36},
    {35, 36, 37, 38},     {37, 36, 39, 38},    {37, 38, 39, 40},
    {25, 31, 36, 39},     {18, 34, 35, 30},    {17, 22, 30, 33},
    {19, 29, 21, 20},     {16, 26, 29, 17},    {24, 29, 28, 25},
    {22, 31, 28, 23},     {20, 31, 30, 21},    {18, 31, 30, 19},
    {16, 30, 17, 17},     {-21, -22, 35, 34},  {-21, -22, 33, 32},
    {-21, -22, 31, 30},   {-21, -22, 29, 28},  {-21, -22, 27, 26},
    {-28, -22, 25, 31},   {24, 28, 29, 30},    {23, 24, 26, 27},
    {23, 24, 25, 25},     {-69, -35, -32, 27}, {-70, 26, 25, -66},
    {-68, -67, 24, -33},
};

static const float monkeyuvs[] = {
    0.898516f, 0.615168f, 0.889924f, 0.593688f, 0.917217f, 0.563713f, 0.938774f, 0.614911f,
    0.816475f, 0.815667f, 0.842653f, 0.844479f, 0.834061f, 0.865960f, 0.795398f, 0.865726f,
    0.938774f, 0.614911f, 0.917217f, 0.563713f, 0.951453f, 0.532635f, 0.985674f, 0.614766f,
    0.781112f, 0.783437f, 0.816475f, 0.815667f, 0.795398f, 0.865726f, 0.746891f, 0.865568f,
    0.917217f, 0.563713f, 0.866019f, 0.542156f, 0.865901f, 0.498414f, 0.951453f, 0.532635f,
    0.866664f, 0.749216f, 0.866533f, 0.794590f, 0.816475f, 0.815667f, 0.781112f, 0.783437f,
    0.889924f, 0.593688f, 0.866296f, 0.585096f, 0.866019f, 0.542156f, 0.917217f, 0.563713f,
    0.866533f, 0.794590f, 0.866281f, 0.835888f, 0.842653f, 0.844479f, 0.816475f, 0.815667f,
    0.866296f, 0.585096f, 0.844815f, 0.593688f, 0.814821f, 0.563713f, 0.866019f, 0.542156f,
    0.916591f, 0.815667f, 0.887762f, 0.844479f, 0.866281f, 0.835888f, 0.866533f, 0.794590f,
    0.866019f, 0.542156f, 0.814821f, 0.563713f, 0.780348f, 0.532635f, 0.865901f, 0.498414f,
    0.952217f, 0.783437f, 0.916591f, 0.815667f, 0.866533f, 0.794590f, 0.866664f, 0.749216f,
    0.814821f, 0.563713f, 0.793264f, 0.614911f, 0.746127f, 0.614766f, 0.780348f, 0.532635f,
    0.986437f, 0.865568f, 0.937668f, 0.865726f, 0.916591f, 0.815667f, 0.952217f, 0.783437f,
    0.844815f, 0.593688f, 0.836223f, 0.615168f, 0.793264f, 0.614911f, 0.814821f, 0.563713f,
    0.937668f, 0.865726f, 0.896353f, 0.865960f, 0.887762f, 0.844479f, 0.916591f, 0.815667f,
    0.836223f, 0.615168f, 0.844815f, 0.638796f, 0.814821f, 0.666108f, 0.793264f, 0.614911f,
    0.916591f, 0.915784f, 0.887762f, 0.889588f, 0.896353f, 0.865960f, 0.937668f, 0.865726f,
    0.793264f, 0.614911f, 0.814821f, 0.666108f, 0.780348f, 0.700318f, 0.746127f, 0.614766f,
    0.952217f, 0.951120f, 0.916591f, 0.915784f, 0.937668f, 0.865726f, 0.986437f, 0.865568f,
    0.814821f, 0.666108f, 0.866019f, 0.687665f, 0.865901f, 0.734539f, 0.780348f, 0.700318f,
    0.866664f, 0.985341f, 0.866533f, 0.936861f, 0.916591f, 0.915784f, 0.952217f, 0.951120f,
    0.844815f, 0.638796f, 0.866296f, 0.647388f, 0.866019f, 0.687665f, 0.814821f, 0.666108f,
    0.866533f, 0.936861f, 0.866281f, 0.898180f, 0.887762f, 0.889588f, 0.916591f, 0.915784f,
    0.866296f, 0.647388f, 0.889924f, 0.638796f, 0.917217f, 0.666108f, 0.866019f, 0.687665f,
    0.816475f, 0.915784f, 0.842653f, 0.889588f, 0.866281f, 0.898180f, 0.866533f, 0.936861f,
    0.866019f, 0.687665f, 0.917217f, 0.666108f, 0.951453f, 0.700318f, 0.865901f, 0.734539f,
    0.781112f, 0.951120f, 0.816475f, 0.915784f, 0.866533f, 0.936861f, 0.866664f, 0.985341f,
    0.917217f, 0.666108f, 0.938774f, 0.614911f, 0.985674f, 0.614766f, 0.951453f, 0.700318f,
    0.746891f, 0.865568f, 0.795398f, 0.865726f, 0.816475f, 0.915784f, 0.781112f, 0.951120f,
    0.889924f, 0.638796f, 0.898516f, 0.615168f, 0.938774f, 0.614911f, 0.917217f, 0.666108f,
    0.795398f, 0.865726f, 0.834061f, 0.865960f, 0.842653f, 0.889588f, 0.816475f, 0.915784f,
    0.898516f, 0.615168f, 0.889924f, 0.638796f, 0.884573f, 0.633599f, 0.890680f, 0.615278f,
    0.847994f, 0.884400f, 0.842653f, 0.889588f, 0.834061f, 0.865960f, 0.841887f, 0.866078f,
    0.889924f, 0.638796f, 0.866296f, 0.647388f, 0.866252f, 0.641233f, 0.884573f, 0.633599f,
    0.866315f, 0.892033f, 0.866281f, 0.898180f, 0.842653f, 0.889588f, 0.847994f, 0.884400f,
    0.866296f, 0.647388f, 0.844815f, 0.638796f, 0.849457f, 0.633599f, 0.866252f, 0.641233f,
    0.883109f, 0.884400f, 0.887762f, 0.889588f, 0.866281f, 0.898180f, 0.866315f, 0.892033f,
    0.844815f, 0.638796f, 0.836223f, 0.615168f, 0.841823f, 0.615278f, 0.849457f, 0.633599f,
    0.890743f, 0.866078f, 0.896353f, 0.865960f, 0.887762f, 0.889588f, 0.883109f, 0.884400f,
    0.836223f, 0.615168f, 0.844815f, 0.593688f, 0.849457f, 0.598483f, 0.841823f, 0.615278f,
    0.883109f, 0.849284f, 0.887762f, 0.844479f, 0.896353f, 0.865960f, 0.890743f, 0.866078f,
    0.844815f, 0.593688f, 0.866296f, 0.585096f, 0.866252f, 0.590849f, 0.849457f, 0.598483f,
    0.866315f, 0.841650f, 0.866281f, 0.835888f, 0.887762f, 0.844479f, 0.883109f, 0.849284f,
    0.866296f, 0.585096f, 0.889924f, 0.593688f, 0.884573f, 0.598483f, 0.866252f, 0.590849f,
    0.847994f, 0.849284f, 0.842653f, 0.844479f, 0.866281f, 0.835888f, 0.866315f, 0.841650f,
    0.889924f, 0.593688f, 0.898516f, 0.615168f, 0.890680f, 0.615278f, 0.884573f, 0.598483f,
    0.841887f, 0.866078f, 0.834061f, 0.865960f, 0.842653f, 0.844479f, 0.847994f, 0.849284f,
    0.866252f, 0.615278f, 0.884573f, 0.598483f, 0.890680f, 0.615278f, 0.841887f, 0.866078f,
    0.847994f, 0.849284f, 0.866314f, 0.866078f, 0.866252f, 0.590849f, 0.884573f, 0.598483f,
    0.866252f, 0.615278f, 0.866314f, 0.866078f, 0.847994f, 0.849284f, 0.866315f, 0.841650f,
    0.866252f, 0.615278f, 0.849457f, 0.598483f, 0.866252f, 0.590849f, 0.866315f, 0.841650f,
    0.883109f, 0.849284f, 0.866314f, 0.866078f, 0.866252f, 0.615278f, 0.841823f, 0.615278f,
    0.849457f, 0.598483f, 0.883109f, 0.849284f, 0.890743f, 0.866078f, 0.866314f, 0.866078f,
    0.866252f, 0.615278f, 0.849457f, 0.633599f, 0.841823f, 0.615278f, 0.890743f, 0.866078f,
    0.883109f, 0.884400f, 0.866314f, 0.866078f, 0.866252f, 0.615278f, 0.866252f, 0.641233f,
    0.849457f, 0.633599f, 0.883109f, 0.884400f, 0.866315f, 0.892033f, 0.866314f, 0.866078f,
    0.866252f, 0.615278f, 0.884573f, 0.633599f, 0.866252f, 0.641233f, 0.866315f, 0.892033f,
    0.847994f, 0.884400f, 0.866314f, 0.866078f, 0.866252f, 0.615278f, 0.890680f, 0.615278f,
    0.884573f, 0.633599f, 0.847994f, 0.884400f, 0.841887f, 0.866078f, 0.866314f, 0.866078f,
    0.519175f, 0.060448f, 0.515511f, 0.068449f, 0.499236f, 0.064527f, 0.499226f, 0.054411f,
    0.499236f, 0.064527f, 0.482972f, 0.068490f, 0.479289f, 0.060503f, 0.499226f, 0.054411f,
    0.528933f, 0.064984f, 0.521101f, 0.070625f, 0.515511f, 0.068449f, 0.519175f, 0.060448f,
    0.482972f, 0.068490f, 0.477388f, 0.070678f, 0.469543f, 0.065062f, 0.479289f, 0.060503f,
    0.531361f, 0.067741f, 0.524816f, 0.079366f, 0.521101f, 0.070625f, 0.528933f, 0.064984f,
    0.477388f, 0.070678f, 0.473699f, 0.079422f, 0.467122f, 0.067822f, 0.469543f, 0.065062f,
    0.543230f, 0.082916f, 0.527778f, 0.091040f, 0.524816f, 0.079366f, 0.531361f, 0.067741f,
    0.473699f, 0.079422f, 0.470772f, 0.091098f, 0.455305f, 0.083023f, 0.467122f, 0.067822f,
    0.564295f, 0.115333f, 0.536201f, 0.127706f, 0.527778f, 0.091040f, 0.543230f, 0.082916f,
    0.470772f, 0.091098f, 0.462464f, 0.127779f, 0.434353f, 0.115495f, 0.455305f, 0.083023f,
    0.585741f, 0.171832f, 0.625570f, 0.207851f, 0.599470f, 0.238064f, 0.534775f, 0.215506f,
    0.399530f, 0.238288f, 0.373395f, 0.208159f, 0.413084f, 0.172042f, 0.464115f, 0.215568f,
    0.625570f, 0.207851f, 0.649992f, 0.239957f, 0.636068f, 0.260938f, 0.599470f, 0.238064f,
    0.363038f, 0.261221f, 0.349092f, 0.240294f, 0.373395f, 0.208159f, 0.399530f, 0.238288f,
    0.649992f, 0.239957f, 0.688725f, 0.268431f, 0.655084f, 0.284130f, 0.636068f, 0.260938f,
    0.344104f, 0.284413f, 0.310499f, 0.268805f, 0.349092f, 0.240294f, 0.363038f, 0.261221f,
    0.688725f, 0.268431f, 0.683826f, 0.317538f, 0.658753f, 0.312921f, 0.655084f, 0.284130f,
    0.340493f, 0.313152f, 0.315483f, 0.317795f, 0.310499f, 0.268805f, 0.344104f, 0.284413f,
    0.683826f, 0.317538f, 0.670653f, 0.334491f, 0.648344f, 0.322238f, 0.658753f, 0.312921f,
    0.350893f, 0.322437f, 0.328643f, 0.334694f, 0.315483f, 0.317795f, 0.340493f, 0.313152f,
    0.670653f, 0.334491f, 0.636036f, 0.370629f, 0.612842f, 0.353000f, 0.648344f, 0.322238f,
    0.386364f, 0.353108f, 0.363225f, 0.370732f, 0.328643f, 0.334694f, 0.350893f, 0.322437f,
    0.636036f, 0.370629f, 0.601484f, 0.395542f, 0.593375f, 0.371207f, 0.612842f, 0.353000f,
    0.405822f, 0.371277f, 0.397739f, 0.395588f, 0.363225f, 0.370732f, 0.386364f, 0.353108f,
    0.601484f, 0.395542f, 0.580245f, 0.398434f, 0.576532f, 0.373285f, 0.593375f, 0.371207f,
    0.422649f, 0.373341f, 0.418956f, 0.398468f, 0.397739f, 0.395588f, 0.405822f, 0.371277f,
    0.580245f, 0.398434f, 0.531177f, 0.387347f, 0.538956f, 0.354297f, 0.576532f, 0.373285f,
    0.460183f, 0.354339f, 0.467984f, 0.387365f, 0.418956f, 0.398468f, 0.422649f, 0.373341f,
    0.531177f, 0.387347f, 0.499576f, 0.382860f, 0.499559f, 0.340417f, 0.538956f, 0.354297f,
    0.499559f, 0.340417f, 0.499576f, 0.382860f, 0.467984f, 0.387365f, 0.460183f, 0.354339f,
    0.558263f, 0.334249f, 0.578201f, 0.335550f, 0.576532f, 0.373285f, 0.538956f, 0.354297f,
    0.422649f, 0.373341f, 0.420943f, 0.335646f, 0.440865f, 0.334326f, 0.460183f, 0.354339f,
    0.578201f, 0.335550f, 0.591245f, 0.332772f, 0.593375f, 0.371207f, 0.576532f, 0.373285f,
    0.405822f, 0.371277f, 0.407910f, 0.332885f, 0.420943f, 0.335646f, 0.422649f, 0.373341f,
    0.605328f, 0.325379f, 0.612842f, 0.353000f, 0.593375f, 0.371207f, 0.591245f, 0.332772f,
    0.405822f, 0.371277f, 0.386364f, 0.353108f, 0.393837f, 0.325516f, 0.407910f, 0.332885f,
    0.627435f, 0.311447f, 0.648344f, 0.322238f, 0.612842f, 0.353000f, 0.605328f, 0.325379f,
    0.386364f, 0.353108f, 0.350893f, 0.322437f, 0.371749f, 0.311636f, 0.393837f, 0.325516f,
    0.634730f, 0.300287f, 0.658753f, 0.312921f, 0.648344f, 0.322238f, 0.627435f, 0.311447f,
    0.350893f, 0.322437f, 0.340493f, 0.313152f, 0.364449f, 0.300507f, 0.371749f, 0.311636f,
    0.630627f, 0.283646f, 0.655084f, 0.284130f, 0.658753f, 0.312921f, 0.634730f, 0.300287f,
    0.340493f, 0.313152f, 0.344104f, 0.284413f, 0.368516f, 0.283889f, 0.364449f, 0.300507f,
    0.610403f, 0.264181f, 0.636068f, 0.260938f, 0.655084f, 0.284130f, 0.630627f, 0.283646f,
    0.344104f, 0.284413f, 0.363038f, 0.261221f, 0.388669f, 0.264411f, 0.368516f, 0.283889f,
    0.591398f, 0.254700f, 0.599470f, 0.238064f, 0.636068f, 0.260938f, 0.610403f, 0.264181f,
    0.363038f, 0.261221f, 0.399530f, 0.238288f, 0.407628f, 0.254897f, 0.388669f, 0.264411f,
    0.558690f, 0.267603f, 0.534775f, 0.215506f, 0.599470f, 0.238064f, 0.591398f, 0.254700f,
    0.399530f, 0.238288f, 0.464115f, 0.215568f, 0.440331f, 0.267717f, 0.407628f, 0.254897f,
    0.558690f, 0.267603f, 0.549915f, 0.277739f, 0.499486f, 0.252445f, 0.534775f, 0.215506f,
    0.499486f, 0.252445f, 0.449122f, 0.277832f, 0.440331f, 0.267717f, 0.464115f, 0.215568f,
    0.558263f, 0.334249f, 0.538956f, 0.354297f, 0.499559f, 0.340417f, 0.543096f, 0.314455f,
    0.499559f, 0.340417f, 0.460183f, 0.354339f, 0.440865f, 0.334326f, 0.455999f, 0.314523f,
    0.543096f, 0.314455f, 0.499559f, 0.340417f, 0.499529f, 0.295691f, 0.542701f, 0.293295f,
    0.499529f, 0.295691f, 0.499559f, 0.340417f, 0.455999f, 0.314523f, 0.456363f, 0.293371f,
    0.499486f, 0.252445f, 0.549915f, 0.277739f, 0.542701f, 0.293295f, 0.499529f, 0.295691f,
    0.456363f, 0.293371f, 0.449122f, 0.277832f, 0.499486f, 0.252445f, 0.499529f, 0.295691f,
    0.505317f, 0.075368f, 0.499246f, 0.073497f, 0.499236f, 0.064527f, 0.515511f, 0.068449f,
    0.499236f, 0.064527f, 0.499246f, 0.073497f, 0.493181f, 0.075382f, 0.482972f, 0.068490f,
    0.510294f, 0.080611f, 0.505317f, 0.075368f, 0.515511f, 0.068449f, 0.521101f, 0.070625f,
    0.482972f, 0.068490f, 0.493181f, 0.075382f, 0.488218f, 0.080633f, 0.477388f, 0.070678f,
    0.510719f, 0.093440f, 0.510294f, 0.080611f, 0.521101f, 0.070625f, 0.524816f, 0.079366f,
    0.477388f, 0.070678f, 0.488218f, 0.080633f, 0.487828f, 0.093457f, 0.473699f, 0.079422f,
    0.536201f, 0.127706f, 0.512928f, 0.133399f, 0.512434f, 0.097171f, 0.527778f, 0.091040f,
    0.486126f, 0.097191f, 0.485743f, 0.133422f, 0.462464f, 0.127779f, 0.470772f, 0.091098f,
    0.510719f, 0.093440f, 0.524816f, 0.079366f, 0.527778f, 0.091040f, 0.512434f, 0.097171f,
    0.470772f, 0.091098f, 0.473699f, 0.079422f, 0.487828f, 0.093457f, 0.486126f, 0.097191f,
    0.518981f, 0.164589f, 0.499367f, 0.157864f, 0.499336f, 0.134352f, 0.512928f, 0.133399f,
    0.499336f, 0.134352f, 0.499367f, 0.157864f, 0.479774f, 0.164615f, 0.485743f, 0.133422f,
    0.512434f, 0.097171f, 0.512928f, 0.133399f, 0.499336f, 0.134352f, 0.499284f, 0.100063f,
    0.499336f, 0.134352f, 0.485743f, 0.133422f, 0.486126f, 0.097191f, 0.499284f, 0.100063f,
    0.499269f, 0.091346f, 0.510719f, 0.093440f, 0.512434f, 0.097171f, 0.499284f, 0.100063f,
    0.486126f, 0.097191f, 0.487828f, 0.093457f, 0.499269f, 0.091346f, 0.499284f, 0.100063f,
    0.529057f, 0.180195f, 0.520697f, 0.181536f, 0.516438f, 0.172335f, 0.518981f, 0.164589f,
    0.482334f, 0.172353f, 0.478099f, 0.181560f, 0.469742f, 0.180239f, 0.479774f, 0.164615f,
    0.522516f, 0.198783f, 0.517786f, 0.194706f, 0.520697f, 0.181536f, 0.529057f, 0.180195f,
    0.478099f, 0.181560f, 0.481043f, 0.194728f, 0.476327f, 0.198817f, 0.469742f, 0.180239f,
    0.499425f, 0.203867f, 0.507044f, 0.197287f, 0.517786f, 0.194706f, 0.522516f, 0.198783f,
    0.481043f, 0.194728f, 0.491789f, 0.197295f, 0.499425f, 0.203867f, 0.476327f, 0.198817f,
    0.499416f, 0.197278f, 0.499409f, 0.192167f, 0.507044f, 0.197287f, 0.499425f, 0.203867f,
    0.491789f, 0.197295f, 0.499409f, 0.192167f, 0.499416f, 0.197278f, 0.499425f, 0.203867f,
    0.499367f, 0.157864f, 0.518981f, 0.164589f, 0.516438f, 0.172335f, 0.499375f, 0.165170f,
    0.482334f, 0.172353f, 0.479774f, 0.164615f, 0.499367f, 0.157864f, 0.499375f, 0.165170f,
    0.499375f, 0.165170f, 0.516438f, 0.172335f, 0.512319f, 0.176937f, 0.499383f, 0.171718f,
    0.486462f, 0.176949f, 0.482334f, 0.172353f, 0.499375f, 0.165170f, 0.499383f, 0.171718f,
    0.499409f, 0.192167f, 0.499401f, 0.186182f, 0.507638f, 0.192399f, 0.507044f, 0.197287f,
    0.491182f, 0.192407f, 0.499401f, 0.186182f, 0.499409f, 0.192167f, 0.491789f, 0.197295f,
    0.507044f, 0.197287f, 0.507638f, 0.192399f, 0.514048f, 0.190694f, 0.517786f, 0.194706f,
    0.484768f, 0.190709f, 0.491182f, 0.192407f, 0.491789f, 0.197295f, 0.481043f, 0.194728f,
    0.517786f, 0.194706f, 0.514048f, 0.190694f, 0.515702f, 0.181858f, 0.520697f, 0.181536f,
    0.483092f, 0.181873f, 0.484768f, 0.190709f, 0.481043f, 0.194728f, 0.478099f, 0.181560f,
    0.520697f, 0.181536f, 0.515702f, 0.181858f, 0.512319f, 0.176937f, 0.516438f, 0.172335f,
    0.486462f, 0.176949f, 0.483092f, 0.181873f, 0.478099f, 0.181560f, 0.482334f, 0.172353f,
    0.499401f, 0.186182f, 0.515702f, 0.181858f, 0.514048f, 0.190694f, 0.507638f, 0.192399f,
    0.484768f, 0.190709f, 0.483092f, 0.181873f, 0.499401f, 0.186182f, 0.491182f, 0.192407f,
    0.499401f, 0.186182f, 0.499383f, 0.171718f, 0.512319f, 0.176937f, 0.515702f, 0.181858f,
    0.486462f, 0.176949f, 0.499383f, 0.171718f, 0.499401f, 0.186182f, 0.483092f, 0.181873f,
    0.499425f, 0.203867f, 0.522516f, 0.198783f, 0.534775f, 0.215506f, 0.499486f, 0.252445f,
    0.464115f, 0.215568f, 0.476327f, 0.198817f, 0.499425f, 0.203867f, 0.499486f, 0.252445f,
    0.522516f, 0.198783f, 0.529057f, 0.180195f, 0.542999f, 0.172734f, 0.534775f, 0.215506f,
    0.455791f, 0.172814f, 0.469742f, 0.180239f, 0.476327f, 0.198817f, 0.464115f, 0.215568f,
    0.529057f, 0.180195f, 0.518981f, 0.164589f, 0.540067f, 0.154824f, 0.542999f, 0.172734f,
    0.458673f, 0.154899f, 0.479774f, 0.164615f, 0.469742f, 0.180239f, 0.455791f, 0.172814f,
    0.518981f, 0.164589f, 0.512928f, 0.133399f, 0.536201f, 0.127706f, 0.540067f, 0.154824f,
    0.462464f, 0.127779f, 0.485743f, 0.133422f, 0.479774f, 0.164615f, 0.458673f, 0.154899f,
    0.564295f, 0.115333f, 0.577388f, 0.138981f, 0.540067f, 0.154824f, 0.536201f, 0.127706f,
    0.458673f, 0.154899f, 0.421339f, 0.139176f, 0.434353f, 0.115495f, 0.462464f, 0.127779f,
    0.577388f, 0.138981f, 0.582019f, 0.153952f, 0.542999f, 0.172734f, 0.540067f, 0.154824f,
    0.455791f, 0.172814f, 0.416755f, 0.154155f, 0.421339f, 0.139176f, 0.458673f, 0.154899f,
    0.585741f, 0.171832f, 0.534775f, 0.215506f, 0.542999f, 0.172734f, 0.582019f, 0.153952f,
    0.455791f, 0.172814f, 0.464115f, 0.215568f, 0.413084f, 0.172042f, 0.416755f, 0.154155f,
    0.510719f, 0.093440f, 0.499269f, 0.091346f, 0.499267f, 0.089977f, 0.508449f, 0.090711f,
    0.499267f, 0.089977f, 0.499269f, 0.091346f, 0.487828f, 0.093457f, 0.490089f, 0.090724f,
    0.510294f, 0.080611f, 0.510719f, 0.093440f, 0.508449f, 0.090711f, 0.508228f, 0.082219f,
    0.490089f, 0.090724f, 0.487828f, 0.093457f, 0.488218f, 0.080633f, 0.490287f, 0.082236f,
    0.505317f, 0.075368f, 0.510294f, 0.080611f, 0.508228f, 0.082219f, 0.503697f, 0.077074f,
    0.490287f, 0.082236f, 0.488218f, 0.080633f, 0.493181f, 0.075382f, 0.494804f, 0.077083f,
    0.499246f, 0.073497f, 0.505317f, 0.075368f, 0.503697f, 0.077074f, 0.499248f, 0.075473f,
    0.494804f, 0.077083f, 0.493181f, 0.075382f, 0.499246f, 0.073497f, 0.499248f, 0.075473f,
    0.499248f, 0.075473f, 0.503697f, 0.077074f, 0.501954f, 0.080835f, 0.499254f, 0.080320f,
    0.496555f, 0.080840f, 0.494804f, 0.077083f, 0.499248f, 0.075473f, 0.499254f, 0.080320f,
    0.503697f, 0.077074f, 0.508228f, 0.082219f, 0.504281f, 0.083193f, 0.501954f, 0.080835f,
    0.494235f, 0.083202f, 0.490287f, 0.082236f, 0.494804f, 0.077083f, 0.496555f, 0.080840f,
    0.508228f, 0.082219f, 0.508449f, 0.090711f, 0.505348f, 0.086781f, 0.504281f, 0.083193f,
    0.493178f, 0.086791f, 0.490089f, 0.090724f, 0.490287f, 0.082236f, 0.494235f, 0.083202f,
    0.508449f, 0.090711f, 0.499267f, 0.089977f, 0.499261f, 0.085637f, 0.505348f, 0.086781f,
    0.499261f, 0.085637f, 0.499267f, 0.089977f, 0.490089f, 0.090724f, 0.493178f, 0.086791f,
    0.499261f, 0.085637f, 0.499254f, 0.080320f, 0.501954f, 0.080835f, 0.505348f, 0.086781f,
    0.496555f, 0.080840f, 0.499254f, 0.080320f, 0.499261f, 0.085637f, 0.493178f, 0.086791f,
    0.505348f, 0.086781f, 0.501954f, 0.080835f, 0.504281f, 0.083193f, 0.494235f, 0.083202f,
    0.496555f, 0.080840f, 0.493178f, 0.086791f, 0.542701f, 0.293295f, 0.549915f, 0.277739f,
    0.561415f, 0.284224f, 0.558262f, 0.294780f, 0.437639f, 0.284338f, 0.449122f, 0.277832f,
    0.456363f, 0.293371f, 0.440810f, 0.294882f, 0.543096f, 0.314455f, 0.542701f, 0.293295f,
    0.558262f, 0.294780f, 0.562274f, 0.307050f, 0.440810f, 0.294882f, 0.456363f, 0.293371f,
    0.455999f, 0.314523f, 0.436820f, 0.307151f, 0.558263f, 0.334249f, 0.543096f, 0.314455f,
    0.562274f, 0.307050f, 0.569573f, 0.318594f, 0.436820f, 0.307151f, 0.455999f, 0.314523f,
    0.440865f, 0.334326f, 0.429542f, 0.318697f, 0.549915f, 0.277739f, 0.558690f, 0.267603f,
    0.568290f, 0.278113f, 0.561415f, 0.284224f, 0.430757f, 0.278245f, 0.440331f, 0.267717f,
    0.449122f, 0.277832f, 0.437639f, 0.284338f, 0.558690f, 0.267603f, 0.591398f, 0.254700f,
    0.588970f, 0.271589f, 0.568290f, 0.278113f, 0.410086f, 0.271772f, 0.407628f, 0.254897f,
    0.440331f, 0.267717f, 0.430757f, 0.278245f, 0.591398f, 0.254700f, 0.610403f, 0.264181f,
    0.601274f, 0.276977f, 0.588970f, 0.271589f, 0.397810f, 0.277180f, 0.388669f, 0.264411f,
    0.407628f, 0.254897f, 0.410086f, 0.271772f, 0.610403f, 0.264181f, 0.630627f, 0.283646f,
    0.616235f, 0.286959f, 0.601274f, 0.276977f, 0.382890f, 0.287175f, 0.368516f, 0.283889f,
    0.388669f, 0.264411f, 0.397810f, 0.277180f, 0.630627f, 0.283646f, 0.634730f, 0.300287f,
    0.617221f, 0.299000f, 0.616235f, 0.286959f, 0.381926f, 0.299197f, 0.364449f, 0.300507f,
    0.368516f, 0.283889f, 0.382890f, 0.287175f, 0.634730f, 0.300287f, 0.627435f, 0.311447f,
    0.615028f, 0.307259f, 0.617221f, 0.299000f, 0.384129f, 0.307438f, 0.371749f, 0.311636f,
    0.364449f, 0.300507f, 0.381926f, 0.299197f, 0.627435f, 0.311447f, 0.605328f, 0.325379f,
    0.597564f, 0.313582f, 0.615028f, 0.307259f, 0.401576f, 0.313725f, 0.393837f, 0.325516f,
    0.371749f, 0.311636f, 0.384129f, 0.307438f, 0.605328f, 0.325379f, 0.591245f, 0.332772f,
    0.589965f, 0.319671f, 0.597564f, 0.313582f, 0.409172f, 0.319797f, 0.407910f, 0.332885f,
    0.393837f, 0.325516f, 0.401576f, 0.313725f, 0.591245f, 0.332772f, 0.578201f, 0.335550f,
    0.581259f, 0.320877f, 0.589965f, 0.319671f, 0.417870f, 0.320991f, 0.420943f, 0.335646f,
    0.407910f, 0.332885f, 0.409172f, 0.319797f, 0.578201f, 0.335550f, 0.558263f, 0.334249f,
    0.569573f, 0.318594f, 0.581259f, 0.320877f, 0.429542f, 0.318697f, 0.440865f, 0.334326f,
    0.420943f, 0.335646f, 0.417870f, 0.320991f, 0.581259f, 0.320877f, 0.569573f, 0.318594f,
    0.577724f, 0.309274f, 0.583117f, 0.312767f, 0.421385f, 0.309394f, 0.429542f, 0.318697f,
    0.417870f, 0.320991f, 0.416002f, 0.312892f, 0.589965f, 0.319671f, 0.581259f, 0.320877f,
    0.583117f, 0.312767f, 0.588782f, 0.312203f, 0.416002f, 0.312892f, 0.417870f, 0.320991f,
    0.409172f, 0.319797f, 0.410344f, 0.312336f, 0.597564f, 0.313582f, 0.589965f, 0.319671f,
    0.588782f, 0.312203f, 0.595478f, 0.309103f, 0.410344f, 0.312336f, 0.409172f, 0.319797f,
    0.401576f, 0.313725f, 0.403653f, 0.309249f, 0.615028f, 0.307259f, 0.597564f, 0.313582f,
    0.595478f, 0.309103f, 0.605147f, 0.302344f, 0.403653f, 0.309249f, 0.401576f, 0.313725f,
    0.384129f, 0.307438f, 0.393986f, 0.302517f, 0.617221f, 0.299000f, 0.615028f, 0.307259f,
    0.605147f, 0.302344f, 0.606862f, 0.297335f, 0.393986f, 0.302517f, 0.384129f, 0.307438f,
    0.381926f, 0.299197f, 0.392265f, 0.297520f, 0.616235f, 0.286959f, 0.617221f, 0.299000f,
    0.606862f, 0.297335f, 0.606100f, 0.291021f, 0.392265f, 0.297520f, 0.381926f, 0.299197f,
    0.382890f, 0.287175f, 0.393015f, 0.291216f, 0.601274f, 0.276977f, 0.616235f, 0.286959f,
    0.606100f, 0.291021f, 0.597364f, 0.282892f, 0.393015f, 0.291216f, 0.382890f, 0.287175f,
    0.397810f, 0.277180f, 0.401725f, 0.283083f, 0.588970f, 0.271589f, 0.601274f, 0.276977f,
    0.597364f, 0.282892f, 0.588939f, 0.279989f, 0.401725f, 0.283083f, 0.397810f, 0.277180f,
    0.410086f, 0.271772f, 0.410134f, 0.280167f, 0.568290f, 0.278113f, 0.588970f, 0.271589f,
    0.588939f, 0.279989f, 0.575416f, 0.284480f, 0.410134f, 0.280167f, 0.410086f, 0.271772f,
    0.430757f, 0.278245f, 0.423648f, 0.284624f, 0.561415f, 0.284224f, 0.568290f, 0.278113f,
    0.575416f, 0.284480f, 0.571900f, 0.289783f, 0.423648f, 0.284624f, 0.430757f, 0.278245f,
    0.437639f, 0.284338f, 0.427169f, 0.289915f, 0.569573f, 0.318594f, 0.562274f, 0.307050f,
    0.572399f, 0.303223f, 0.577724f, 0.309274f, 0.426693f, 0.303342f, 0.436820f, 0.307151f,
    0.429542f, 0.318697f, 0.421385f, 0.309394f, 0.562274f, 0.307050f, 0.558262f, 0.294780f,
    0.571526f, 0.295591f, 0.572399f, 0.303223f, 0.427553f, 0.295716f, 0.440810f, 0.294882f,
    0.436820f, 0.307151f, 0.426693f, 0.303342f, 0.558262f, 0.294780f, 0.561415f, 0.284224f,
    0.571900f, 0.289783f, 0.571526f, 0.295591f, 0.427169f, 0.289915f, 0.437639f, 0.284338f,
    0.440810f, 0.294882f, 0.427553f, 0.295716f, 0.499576f, 0.382860f, 0.531177f, 0.387347f,
    0.528820f, 0.428638f, 0.499582f, 0.428449f, 0.470353f, 0.428636f, 0.467984f, 0.387365f,
    0.499576f, 0.382860f, 0.499582f, 0.428449f, 0.531177f, 0.387347f, 0.580245f, 0.398434f,
    0.583835f, 0.428591f, 0.528820f, 0.428638f, 0.415384f, 0.428598f, 0.418956f, 0.398468f,
    0.467984f, 0.387365f, 0.470353f, 0.428636f, 0.580245f, 0.398434f, 0.601484f, 0.395542f,
    0.621564f, 0.417863f, 0.583835f, 0.428591f, 0.377699f, 0.417889f, 0.397739f, 0.395588f,
    0.418956f, 0.398468f, 0.415384f, 0.428598f, 0.601484f, 0.395542f, 0.636036f, 0.370629f,
    0.665541f, 0.388861f, 0.621564f, 0.417863f, 0.333786f, 0.388954f, 0.363225f, 0.370732f,
    0.397739f, 0.395588f, 0.377699f, 0.417889f, 0.636036f, 0.370629f, 0.670653f, 0.334491f,
    0.700852f, 0.344252f, 0.665541f, 0.388861f, 0.298519f, 0.344467f, 0.328643f, 0.334694f,
    0.363225f, 0.370732f, 0.333786f, 0.388954f, 0.670653f, 0.334491f, 0.683826f, 0.317538f,
    0.711534f, 0.315224f, 0.700852f, 0.344252f, 0.287837f, 0.315527f, 0.315483f, 0.317795f,
    0.328643f, 0.334694f, 0.298519f, 0.344467f, 0.683826f, 0.317538f, 0.688725f, 0.268431f,
    0.703543f, 0.259804f, 0.711534f, 0.315224f, 0.295695f, 0.260225f, 0.310499f, 0.268805f,
    0.315483f, 0.317795f, 0.287837f, 0.315527f, 0.688725f, 0.268431f, 0.649992f, 0.239957f,
    0.672036f, 0.224472f, 0.703543f, 0.259804f, 0.327053f, 0.224880f, 0.349092f, 0.240294f,
    0.310499f, 0.268805f, 0.295695f, 0.260225f, 0.649992f, 0.239957f, 0.625570f, 0.207851f,
    0.639461f, 0.198488f, 0.672036f, 0.224472f, 0.359503f, 0.198841f, 0.373395f, 0.208159f,
    0.349092f, 0.240294f, 0.327053f, 0.224880f, 0.601923f, 0.023020f, 0.623531f, 0.028944f,
    0.634365f, 0.081724f, 0.597102f, 0.056491f, 0.364247f, 0.082198f, 0.374872f, 0.029461f,
    0.396447f, 0.023452f, 0.401383f, 0.056841f, 0.545071f, 0.009261f, 0.601923f, 0.023020f,
    0.597102f, 0.056491f, 0.554761f, 0.040719f, 0.401383f, 0.056841f, 0.396447f, 0.023452f,
    0.453219f, 0.009442f, 0.443646f, 0.040906f, 0.499203f, 0.033223f, 0.545071f, 0.009261f,
    0.554761f, 0.040719f, 0.525595f, 0.049432f, 0.443646f, 0.040906f, 0.453219f, 0.009442f,
    0.499203f, 0.033223f, 0.472841f, 0.049514f, 0.519175f, 0.060448f, 0.499226f, 0.054411f,
    0.499203f, 0.033223f, 0.525595f, 0.049432f, 0.499203f, 0.033223f, 0.499226f, 0.054411f,
    0.479289f, 0.060503f, 0.472841f, 0.049514f, 0.528933f, 0.064984f, 0.519175f, 0.060448f,
    0.525595f, 0.049432f, 0.535690f, 0.060692f, 0.472841f, 0.049514f, 0.479289f, 0.060503f,
    0.469543f, 0.065062f, 0.462778f, 0.060794f, 0.531361f, 0.067741f, 0.528933f, 0.064984f,
    0.535690f, 0.060692f, 0.556987f, 0.064968f, 0.462778f, 0.060794f, 0.469543f, 0.065062f,
    0.467122f, 0.067822f, 0.441501f, 0.065137f, 0.543230f, 0.082916f, 0.531361f, 0.067741f,
    0.556987f, 0.064968f, 0.586699f, 0.084908f, 0.441501f, 0.065137f, 0.467122f, 0.067822f,
    0.455305f, 0.083023f, 0.411873f, 0.085177f, 0.556987f, 0.064968f, 0.554761f, 0.040719f,
    0.597102f, 0.056491f, 0.586699f, 0.084908f, 0.401383f, 0.056841f, 0.443646f, 0.040906f,
    0.441501f, 0.065137f, 0.411873f, 0.085177f, 0.556987f, 0.064968f, 0.535690f, 0.060692f,
    0.525595f, 0.049432f, 0.554761f, 0.040719f, 0.472841f, 0.049514f, 0.462778f, 0.060794f,
    0.441501f, 0.065137f, 0.443646f, 0.040906f, 0.605919f, 0.113619f, 0.586699f, 0.084908f,
    0.597102f, 0.056491f, 0.634365f, 0.081724f, 0.401383f, 0.056841f, 0.411873f, 0.085177f,
    0.392761f, 0.113940f, 0.364247f, 0.082198f, 0.564295f, 0.115333f, 0.543230f, 0.082916f,
    0.586699f, 0.084908f, 0.605919f, 0.113619f, 0.411873f, 0.085177f, 0.455305f, 0.083023f,
    0.434353f, 0.115495f, 0.392761f, 0.113940f, 0.577388f, 0.138981f, 0.611410f, 0.133352f,
    0.604255f, 0.151220f, 0.582019f, 0.153952f, 0.394533f, 0.151503f, 0.387337f, 0.133675f,
    0.421339f, 0.139176f, 0.416755f, 0.154155f, 0.564295f, 0.115333f, 0.605919f, 0.113619f,
    0.611410f, 0.133352f, 0.577388f, 0.138981f, 0.387337f, 0.133675f, 0.392761f, 0.113940f,
    0.434353f, 0.115495f, 0.421339f, 0.139176f, 0.585741f, 0.171832f, 0.582019f, 0.153952f,
    0.604255f, 0.151220f, 0.599523f, 0.162726f, 0.394533f, 0.151503f, 0.416755f, 0.154155f,
    0.413084f, 0.172042f, 0.399292f, 0.162985f, 0.585741f, 0.171832f, 0.599523f, 0.162726f,
    0.639461f, 0.198488f, 0.625570f, 0.207851f, 0.359503f, 0.198841f, 0.399292f, 0.162985f,
    0.413084f, 0.172042f, 0.373395f, 0.208159f, 0.921561f, 0.062683f, 0.858848f, 0.116790f,
    0.822344f, 0.100435f, 0.860246f, 0.025793f, 0.176713f, 0.101559f, 0.140353f, 0.117994f,
    0.077626f, 0.064228f, 0.138658f, 0.027230f, 0.860246f, 0.025793f, 0.822344f, 0.100435f,
    0.764738f, 0.086041f, 0.773234f, 0.009669f, 0.234137f, 0.087001f, 0.176713f, 0.101559f,
    0.138658f, 0.027230f, 0.225396f, 0.010837f, 0.773234f, 0.009669f, 0.764738f, 0.086041f,
    0.660621f, 0.086994f, 0.645431f, 0.028106f, 0.338048f, 0.087561f, 0.234137f, 0.087001f,
    0.225396f, 0.010837f, 0.352990f, 0.028716f, 0.645431f, 0.028106f, 0.660621f, 0.086994f,
    0.634365f, 0.081724f, 0.623531f, 0.028944f, 0.364247f, 0.082198f, 0.338048f, 0.087561f,
    0.352990f, 0.028716f, 0.374872f, 0.029461f, 0.605919f, 0.113619f, 0.634365f, 0.081724f,
    0.660621f, 0.086994f, 0.611410f, 0.133352f, 0.338048f, 0.087561f, 0.364247f, 0.082198f,
    0.392761f, 0.113940f, 0.387337f, 0.133675f, 0.703543f, 0.259804f, 0.672036f, 0.224472f,
    0.742764f, 0.197916f, 0.765725f, 0.232421f, 0.256408f, 0.198572f, 0.327053f, 0.224880f,
    0.295695f, 0.260225f, 0.233590f, 0.233065f, 0.987287f, 0.152728f, 0.888944f, 0.176456f,
    0.858848f, 0.116790f, 0.921561f, 0.062683f, 0.140353f, 0.117994f, 0.110522f, 0.177602f,
    0.012367f, 0.154243f, 0.077626f, 0.064228f, 0.900013f, 0.420388f, 0.844154f, 0.346818f,
    0.881002f, 0.308389f, 0.959159f, 0.356531f, 0.118831f, 0.309153f, 0.155683f, 0.347370f,
    0.100193f, 0.420886f, 0.041037f, 0.357385f, 0.959159f, 0.356531f, 0.881002f, 0.308389f,
    0.903058f, 0.260405f, 0.999860f, 0.252255f, 0.096698f, 0.261367f, 0.118831f, 0.309153f,
    0.041037f, 0.357385f, 0.000140f, 0.253531f, 0.999860f, 0.252255f, 0.903058f, 0.260405f,
    0.888944f, 0.176456f, 0.987287f, 0.152728f, 0.110522f, 0.177602f, 0.096698f, 0.261367f,
    0.000140f, 0.253531f, 0.012367f, 0.154243f, 0.724230f, 0.339961f, 0.711534f, 0.315224f,
    0.744163f, 0.304222f, 0.765650f, 0.319075f, 0.255275f, 0.304629f, 0.287837f, 0.315527f,
    0.275237f, 0.340212f, 0.233887f, 0.319494f, 0.765650f, 0.319075f, 0.744163f, 0.304222f,
    0.792936f, 0.279898f, 0.826311f, 0.297169f, 0.206573f, 0.280495f, 0.255275f, 0.304629f,
    0.233887f, 0.319494f, 0.173337f, 0.297810f, 0.826311f, 0.297169f, 0.792936f, 0.279898f,
    0.823141f, 0.253863f, 0.854975f, 0.267153f, 0.176380f, 0.254612f, 0.206573f, 0.280495f,
    0.173337f, 0.297810f, 0.144669f, 0.267956f, 0.854975f, 0.267153f, 0.823141f, 0.253863f,
    0.835330f, 0.224364f, 0.867888f, 0.232592f, 0.164140f, 0.225225f, 0.176380f, 0.254612f,
    0.144669f, 0.267956f, 0.131691f, 0.233525f, 0.812814f, 0.189821f, 0.850667f, 0.180124f,
    0.867888f, 0.232592f, 0.835330f, 0.224364f, 0.131691f, 0.233525f, 0.148712f, 0.181145f,
    0.186501f, 0.190705f, 0.164140f, 0.225225f, 0.888944f, 0.176456f, 0.903058f, 0.260405f,
    0.867888f, 0.232592f, 0.850667f, 0.180124f, 0.131691f, 0.233525f, 0.096698f, 0.261367f,
    0.110522f, 0.177602f, 0.148712f, 0.181145f, 0.903058f, 0.260405f, 0.881002f, 0.308389f,
    0.854975f, 0.267153f, 0.867888f, 0.232592f, 0.144669f, 0.267956f, 0.118831f, 0.309153f,
    0.096698f, 0.261367f, 0.131691f, 0.233525f, 0.881002f, 0.308389f, 0.844154f, 0.346818f,
    0.826311f, 0.297169f, 0.854975f, 0.267153f, 0.173337f, 0.297810f, 0.155683f, 0.347370f,
    0.118831f, 0.309153f, 0.144669f, 0.267956f, 0.844154f, 0.346818f, 0.771964f, 0.373733f,
    0.765650f, 0.319075f, 0.826311f, 0.297169f, 0.233887f, 0.319494f, 0.227737f, 0.374010f,
    0.155683f, 0.347370f, 0.173337f, 0.297810f, 0.744181f, 0.389903f, 0.724230f, 0.339961f,
    0.765650f, 0.319075f, 0.771964f, 0.373733f, 0.233887f, 0.319494f, 0.275237f, 0.340212f,
    0.255481f, 0.390057f, 0.227737f, 0.374010f, 0.824891f, 0.470849f, 0.771964f, 0.373733f,
    0.844154f, 0.346818f, 0.900013f, 0.420388f, 0.155683f, 0.347370f, 0.227737f, 0.374010f,
    0.175235f, 0.470987f, 0.100193f, 0.420886f, 0.792153f, 0.464650f, 0.742036f, 0.461609f,
    0.726584f, 0.432597f, 0.744181f, 0.389903f, 0.273145f, 0.432574f, 0.257819f, 0.461543f,
    0.207859f, 0.464714f, 0.255481f, 0.390057f, 0.792153f, 0.464650f, 0.744181f, 0.389903f,
    0.771964f, 0.373733f, 0.824891f, 0.470849f, 0.227737f, 0.374010f, 0.255481f, 0.390057f,
    0.207859f, 0.464714f, 0.175235f, 0.470987f, 0.795638f, 0.489991f, 0.792153f, 0.464650f,
    0.824891f, 0.470849f, 0.175235f, 0.470987f, 0.207859f, 0.464714f, 0.204362f, 0.489991f,
    0.711534f, 0.315224f, 0.703543f, 0.259804f, 0.765725f, 0.232421f, 0.744163f, 0.304222f,
    0.233590f, 0.233065f, 0.295695f, 0.260225f, 0.287837f, 0.315527f, 0.255275f, 0.304629f,
    0.765725f, 0.232421f, 0.785486f, 0.227075f, 0.792936f, 0.279898f, 0.744163f, 0.304222f,
    0.206573f, 0.280495f, 0.213863f, 0.227788f, 0.233590f, 0.233065f, 0.255275f, 0.304629f,
    0.785486f, 0.227075f, 0.802275f, 0.202593f, 0.823141f, 0.253863f, 0.792936f, 0.279898f,
    0.176380f, 0.254612f, 0.197050f, 0.203415f, 0.213863f, 0.227788f, 0.206573f, 0.280495f,
    0.812814f, 0.189821f, 0.835330f, 0.224364f, 0.823141f, 0.253863f, 0.802275f, 0.202593f,
    0.176380f, 0.254612f, 0.164140f, 0.225225f, 0.186501f, 0.190705f, 0.197050f, 0.203415f,
    0.660621f, 0.086994f, 0.764738f, 0.086041f, 0.768354f, 0.127888f, 0.690398f, 0.146106f,
    0.230666f, 0.128771f, 0.234137f, 0.087001f, 0.338048f, 0.087561f, 0.308516f, 0.146686f,
    0.742764f, 0.197916f, 0.690398f, 0.146106f, 0.768354f, 0.127888f, 0.779285f, 0.168550f,
    0.230666f, 0.128771f, 0.308516f, 0.146686f, 0.256408f, 0.198572f, 0.219887f, 0.169386f,
    0.672036f, 0.224472f, 0.639461f, 0.198488f, 0.690398f, 0.146106f, 0.742764f, 0.197916f,
    0.308516f, 0.146686f, 0.359503f, 0.198841f, 0.327053f, 0.224880f, 0.256408f, 0.198572f,
    0.639461f, 0.198488f, 0.604255f, 0.151220f, 0.611410f, 0.133352f, 0.690398f, 0.146106f,
    0.387337f, 0.133675f, 0.394533f, 0.151503f, 0.359503f, 0.198841f, 0.308516f, 0.146686f,
    0.611410f, 0.133352f, 0.660621f, 0.086994f, 0.690398f, 0.146106f, 0.308516f, 0.146686f,
    0.338048f, 0.087561f, 0.387337f, 0.133675f, 0.639461f, 0.198488f, 0.599523f, 0.162726f,
    0.604255f, 0.151220f, 0.394533f, 0.151503f, 0.399292f, 0.162985f, 0.359503f, 0.198841f,
    0.812814f, 0.189821f, 0.803020f, 0.170278f, 0.830115f, 0.138457f, 0.850667f, 0.180124f,
    0.169083f, 0.139518f, 0.196211f, 0.171181f, 0.186501f, 0.190705f, 0.148712f, 0.181145f,
    0.888944f, 0.176456f, 0.850667f, 0.180124f, 0.830115f, 0.138457f, 0.858848f, 0.116790f,
    0.169083f, 0.139518f, 0.148712f, 0.181145f, 0.110522f, 0.177602f, 0.140353f, 0.117994f,
    0.779285f, 0.168550f, 0.768354f, 0.127888f, 0.830115f, 0.138457f, 0.803020f, 0.170278f,
    0.169083f, 0.139518f, 0.230666f, 0.128771f, 0.219887f, 0.169386f, 0.196211f, 0.171181f,
    0.764738f, 0.086041f, 0.822344f, 0.100435f, 0.830115f, 0.138457f, 0.768354f, 0.127888f,
    0.169083f, 0.139518f, 0.176713f, 0.101559f, 0.234137f, 0.087001f, 0.230666f, 0.128771f,
    0.858848f, 0.116790f, 0.830115f, 0.138457f, 0.822344f, 0.100435f, 0.176713f, 0.101559f,
    0.169083f, 0.139518f, 0.140353f, 0.117994f, 0.560056f, 0.957116f, 0.449940f, 0.959188f,
    0.462753f, 0.891851f, 0.530867f, 0.884581f, 0.272722f, 0.891853f, 0.285528f, 0.959188f,
    0.175421f, 0.957118f, 0.204610f, 0.884582f, 0.560056f, 0.957116f, 0.530867f, 0.884581f,
    0.590562f, 0.846978f, 0.701323f, 0.897070f, 0.144915f, 0.846977f, 0.204610f, 0.884582f,
    0.175421f, 0.957118f, 0.034155f, 0.897069f, 0.701323f, 0.897070f, 0.590562f, 0.846978f,
    0.590787f, 0.784411f, 0.669298f, 0.757013f, 0.144687f, 0.784410f, 0.144915f, 0.846977f,
    0.034155f, 0.897069f, 0.066170f, 0.757009f, 0.669298f, 0.757013f, 0.590787f, 0.784411f,
    0.560262f, 0.744213f, 0.603293f, 0.719157f, 0.175212f, 0.744211f, 0.144687f, 0.784410f,
    0.066170f, 0.757009f, 0.132179f, 0.719155f, 0.603293f, 0.719157f, 0.560262f, 0.744213f,
    0.520423f, 0.712528f, 0.554154f, 0.682132f, 0.215052f, 0.712528f, 0.175212f, 0.744211f,
    0.132179f, 0.719155f, 0.181319f, 0.682131f, 0.554154f, 0.682132f, 0.520423f, 0.712528f,
    0.468433f, 0.686792f, 0.493965f, 0.641024f, 0.267042f, 0.686791f, 0.215052f, 0.712528f,
    0.181319f, 0.682131f, 0.241508f, 0.641024f, 0.520423f, 0.712528f, 0.498843f, 0.736977f,
    0.464308f, 0.719593f, 0.468433f, 0.686792f, 0.271168f, 0.719592f, 0.236633f, 0.736977f,
    0.215052f, 0.712528f, 0.267042f, 0.686791f, 0.560262f, 0.744213f, 0.526545f, 0.760754f,
    0.498843f, 0.736977f, 0.520423f, 0.712528f, 0.236633f, 0.736977f, 0.208931f, 0.760754f,
    0.175212f, 0.744211f, 0.215052f, 0.712528f, 0.590787f, 0.784411f, 0.543027f, 0.789442f,
    0.526545f, 0.760754f, 0.560262f, 0.744213f, 0.208931f, 0.760754f, 0.192449f, 0.789442f,
    0.144687f, 0.784410f, 0.175212f, 0.744211f, 0.590562f, 0.846978f, 0.539269f, 0.821849f,
    0.543027f, 0.789442f, 0.590787f, 0.784411f, 0.192449f, 0.789442f, 0.196208f, 0.821849f,
    0.144915f, 0.846977f, 0.144687f, 0.784410f, 0.530867f, 0.884581f, 0.510099f, 0.842380f,
    0.539269f, 0.821849f, 0.590562f, 0.846978f, 0.196208f, 0.821849f, 0.225378f, 0.842380f,
    0.204610f, 0.884582f, 0.144915f, 0.846977f, 0.530867f, 0.884581f, 0.462753f, 0.891851f,
    0.470621f, 0.845320f, 0.510099f, 0.842380f, 0.264856f, 0.845322f, 0.272722f, 0.891853f,
    0.204610f, 0.884582f, 0.225378f, 0.842380f, 0.742764f, 0.197916f, 0.779285f, 0.168550f,
    0.785370f, 0.180300f, 0.769420f, 0.190471f, 0.213850f, 0.181127f, 0.219887f, 0.169386f,
    0.256408f, 0.198572f, 0.229792f, 0.191225f, 0.393578f, 0.587505f, 0.493965f, 0.641024f,
    0.468433f, 0.686792f, 0.395036f, 0.674023f, 0.267042f, 0.686791f, 0.241508f, 0.641024f,
    0.341892f, 0.587509f, 0.340437f, 0.674021f, 0.765725f, 0.232421f, 0.742764f, 0.197916f,
    0.769420f, 0.190471f, 0.785486f, 0.227075f, 0.229792f, 0.191225f, 0.256408f, 0.198572f,
    0.233590f, 0.233065f, 0.213863f, 0.227788f, 0.385726f, 0.910555f, 0.399584f, 0.878316f,
    0.462753f, 0.891851f, 0.449940f, 0.959188f, 0.272722f, 0.891853f, 0.335895f, 0.878320f,
    0.349744f, 0.910558f, 0.285528f, 0.959188f, 0.468433f, 0.686792f, 0.464308f, 0.719593f,
    0.429058f, 0.713731f, 0.395036f, 0.674023f, 0.306418f, 0.713731f, 0.271168f, 0.719592f,
    0.267042f, 0.686791f, 0.340437f, 0.674021f, 0.415153f, 0.728622f, 0.378240f, 0.710772f,
    0.395036f, 0.674023f, 0.429058f, 0.713731f, 0.340437f, 0.674021f, 0.357237f, 0.710776f,
    0.320324f, 0.728623f, 0.306418f, 0.713731f, 0.408407f, 0.754607f, 0.378240f, 0.710772f,
    0.415153f, 0.728622f, 0.418068f, 0.746526f, 0.320324f, 0.728623f, 0.357237f, 0.710776f,
    0.327070f, 0.754610f, 0.317409f, 0.746528f, 0.413147f, 0.796172f, 0.377006f, 0.815186f,
    0.378240f, 0.710772f, 0.408407f, 0.754607f, 0.357237f, 0.710776f, 0.358471f, 0.815189f,
    0.322330f, 0.796174f, 0.327070f, 0.754610f, 0.399584f, 0.878316f, 0.377006f, 0.815186f,
    0.413147f, 0.796172f, 0.433705f, 0.830975f, 0.322330f, 0.796174f, 0.358471f, 0.815189f,
    0.335895f, 0.878320f, 0.301772f, 0.830977f, 0.462753f, 0.891851f, 0.399584f, 0.878316f,
    0.433705f, 0.830975f, 0.470621f, 0.845320f, 0.301772f, 0.830977f, 0.335895f, 0.878320f,
    0.272722f, 0.891853f, 0.264856f, 0.845322f, 0.785486f, 0.227075f, 0.797000f, 0.194826f,
    0.801893f, 0.198928f, 0.802275f, 0.202593f, 0.197420f, 0.199758f, 0.202290f, 0.195652f,
    0.213863f, 0.227788f, 0.197050f, 0.203415f, 0.785486f, 0.227075f, 0.769420f, 0.190471f,
    0.788848f, 0.183842f, 0.797000f, 0.194826f, 0.210391f, 0.184671f, 0.229792f, 0.191225f,
    0.213863f, 0.227788f, 0.202290f, 0.195652f, 0.769420f, 0.190471f, 0.785370f, 0.180300f,
    0.788848f, 0.183842f, 0.210391f, 0.184671f, 0.213850f, 0.181127f, 0.229792f, 0.191225f,
    0.470621f, 0.845320f, 0.433705f, 0.830975f, 0.451471f, 0.804090f, 0.475926f, 0.816075f,
    0.284007f, 0.804092f, 0.301772f, 0.830977f, 0.264856f, 0.845322f, 0.259551f, 0.816076f,
    0.433705f, 0.830975f, 0.413147f, 0.796172f, 0.433470f, 0.783920f, 0.451471f, 0.804090f,
    0.302007f, 0.783922f, 0.322330f, 0.796174f, 0.301772f, 0.830977f, 0.284007f, 0.804092f,
    0.413147f, 0.796172f, 0.408407f, 0.754607f, 0.424174f, 0.764959f, 0.433470f, 0.783920f,
    0.311304f, 0.764961f, 0.327070f, 0.754610f, 0.322330f, 0.796174f, 0.302007f, 0.783922f,
    0.408407f, 0.754607f, 0.418068f, 0.746526f, 0.430644f, 0.756350f, 0.424174f, 0.764959f,
    0.304833f, 0.756352f, 0.317409f, 0.746528f, 0.327070f, 0.754610f, 0.311304f, 0.764961f,
    0.418068f, 0.746526f, 0.415153f, 0.728622f, 0.435247f, 0.743457f, 0.430644f, 0.756350f,
    0.300230f, 0.743459f, 0.320324f, 0.728623f, 0.317409f, 0.746528f, 0.304833f, 0.756352f,
    0.415153f, 0.728622f, 0.429058f, 0.713731f, 0.441735f, 0.732686f, 0.435247f, 0.743457f,
    0.293742f, 0.732687f, 0.306418f, 0.713731f, 0.320324f, 0.728623f, 0.300230f, 0.743459f,
    0.429058f, 0.713731f, 0.464308f, 0.719593f, 0.460899f, 0.740871f, 0.441735f, 0.732686f,
    0.274578f, 0.740871f, 0.271168f, 0.719592f, 0.306418f, 0.713731f, 0.293742f, 0.732687f,
    0.510099f, 0.842380f, 0.470621f, 0.845320f, 0.475926f, 0.816075f, 0.499639f, 0.817768f,
    0.259551f, 0.816076f, 0.264856f, 0.845322f, 0.225378f, 0.842380f, 0.235838f, 0.817769f,
    0.539269f, 0.821849f, 0.510099f, 0.842380f, 0.499639f, 0.817768f, 0.513389f, 0.808597f,
    0.235838f, 0.817769f, 0.225378f, 0.842380f, 0.196208f, 0.821849f, 0.222088f, 0.808598f,
    0.543027f, 0.789442f, 0.539269f, 0.821849f, 0.513389f, 0.808597f, 0.515507f, 0.790754f,
    0.222088f, 0.808598f, 0.196208f, 0.821849f, 0.192449f, 0.789442f, 0.219970f, 0.790754f,
    0.526545f, 0.760754f, 0.543027f, 0.789442f, 0.515507f, 0.790754f, 0.504048f, 0.772385f,
    0.219970f, 0.790754f, 0.192449f, 0.789442f, 0.208931f, 0.760754f, 0.231428f, 0.772385f,
    0.498843f, 0.736977f, 0.526545f, 0.760754f, 0.504048f, 0.772385f, 0.484096f, 0.755033f,
    0.231428f, 0.772385f, 0.208931f, 0.760754f, 0.236633f, 0.736977f, 0.251380f, 0.755033f,
    0.464308f, 0.719593f, 0.498843f, 0.736977f, 0.484096f, 0.755033f, 0.460899f, 0.740871f,
    0.251380f, 0.755033f, 0.236633f, 0.736977f, 0.271168f, 0.719592f, 0.274578f, 0.740871f,
    0.430644f, 0.756350f, 0.435247f, 0.743457f, 0.453457f, 0.756171f, 0.445088f, 0.770458f,
    0.282020f, 0.756172f, 0.300230f, 0.743459f, 0.304833f, 0.756352f, 0.290389f, 0.770460f,
    0.445088f, 0.770458f, 0.453457f, 0.756171f, 0.472595f, 0.769899f, 0.462782f, 0.785392f,
    0.262882f, 0.769899f, 0.282020f, 0.756172f, 0.290389f, 0.770460f, 0.272695f, 0.785393f,
    0.462782f, 0.785392f, 0.472595f, 0.769899f, 0.490045f, 0.782999f, 0.481973f, 0.797121f,
    0.245432f, 0.782999f, 0.262882f, 0.769899f, 0.272695f, 0.785393f, 0.253504f, 0.797122f,
    0.481973f, 0.797121f, 0.490045f, 0.782999f, 0.501565f, 0.792591f, 0.499142f, 0.804019f,
    0.233912f, 0.792591f, 0.245432f, 0.782999f, 0.253504f, 0.797122f, 0.236336f, 0.804020f,
    0.499639f, 0.817768f, 0.475926f, 0.816075f, 0.481973f, 0.797121f, 0.499142f, 0.804019f,
    0.253504f, 0.797122f, 0.259551f, 0.816076f, 0.235838f, 0.817769f, 0.236336f, 0.804020f,
    0.451471f, 0.804090f, 0.462782f, 0.785392f, 0.481973f, 0.797121f, 0.475926f, 0.816075f,
    0.253504f, 0.797122f, 0.272695f, 0.785393f, 0.284007f, 0.804092f, 0.259551f, 0.816076f,
    0.451471f, 0.804090f, 0.433470f, 0.783920f, 0.445088f, 0.770458f, 0.462782f, 0.785392f,
    0.290389f, 0.770460f, 0.302007f, 0.783922f, 0.284007f, 0.804092f, 0.272695f, 0.785393f,
    0.430644f, 0.756350f, 0.445088f, 0.770458f, 0.433470f, 0.783920f, 0.424174f, 0.764959f,
    0.302007f, 0.783922f, 0.290389f, 0.770460f, 0.304833f, 0.756352f, 0.311304f, 0.764961f,
    0.441735f, 0.732686f, 0.460899f, 0.740871f, 0.453457f, 0.756171f, 0.435247f, 0.743457f,
    0.282020f, 0.756172f, 0.274578f, 0.740871f, 0.293742f, 0.732687f, 0.300230f, 0.743459f,
    0.484096f, 0.755033f, 0.472595f, 0.769899f, 0.453457f, 0.756171f, 0.460899f, 0.740871f,
    0.282020f, 0.756172f, 0.262882f, 0.769899f, 0.251380f, 0.755033f, 0.274578f, 0.740871f,
    0.504048f, 0.772385f, 0.490045f, 0.782999f, 0.472595f, 0.769899f, 0.484096f, 0.755033f,
    0.262882f, 0.769899f, 0.245432f, 0.782999f, 0.231428f, 0.772385f, 0.251380f, 0.755033f,
    0.515507f, 0.790754f, 0.501565f, 0.792591f, 0.490045f, 0.782999f, 0.504048f, 0.772385f,
    0.245432f, 0.782999f, 0.233912f, 0.792591f, 0.219970f, 0.790754f, 0.231428f, 0.772385f,
    0.513389f, 0.808597f, 0.499142f, 0.804019f, 0.501565f, 0.792591f, 0.515507f, 0.790754f,
    0.233912f, 0.792591f, 0.236336f, 0.804020f, 0.222088f, 0.808598f, 0.219970f, 0.790754f,
    0.499639f, 0.817768f, 0.499142f, 0.804019f, 0.513389f, 0.808597f, 0.222088f, 0.808598f,
    0.236336f, 0.804020f, 0.235838f, 0.817769f, 0.554154f, 0.682132f, 0.493965f, 0.641024f,
    0.559696f, 0.607669f, 0.598936f, 0.655817f, 0.175778f, 0.607667f, 0.241508f, 0.641024f,
    0.181319f, 0.682131f, 0.136537f, 0.655816f, 0.603293f, 0.719157f, 0.554154f, 0.682132f,
    0.598936f, 0.655817f, 0.629457f, 0.693271f, 0.136537f, 0.655816f, 0.181319f, 0.682131f,
    0.132179f, 0.719155f, 0.106015f, 0.693269f, 0.669298f, 0.757013f, 0.603293f, 0.719157f,
    0.629457f, 0.693271f, 0.653588f, 0.712098f, 0.106015f, 0.693269f, 0.132179f, 0.719155f,
    0.066170f, 0.757009f, 0.081883f, 0.712096f, 0.712556f, 0.698473f, 0.669298f, 0.757013f,
    0.653588f, 0.712098f, 0.662010f, 0.686010f, 0.081883f, 0.712096f, 0.066170f, 0.757009f,
    0.022915f, 0.698478f, 0.073462f, 0.686010f, 0.715231f, 0.624373f, 0.712556f, 0.698473f,
    0.662010f, 0.686010f, 0.652439f, 0.640199f, 0.073462f, 0.686010f, 0.022915f, 0.698478f,
    0.020244f, 0.624367f, 0.083034f, 0.640197f, 0.706720f, 0.572934f, 0.715231f, 0.624373f,
    0.652439f, 0.640199f, 0.633578f, 0.594596f, 0.083034f, 0.640197f, 0.020244f, 0.624367f,
    0.028756f, 0.572934f, 0.101895f, 0.594596f, 0.652439f, 0.640199f, 0.598936f, 0.655817f,
    0.559696f, 0.607669f, 0.633578f, 0.594596f, 0.175778f, 0.607667f, 0.136537f, 0.655816f,
    0.083034f, 0.640197f, 0.101895f, 0.594596f, 0.652439f, 0.640199f, 0.662010f, 0.686010f,
    0.629457f, 0.693271f, 0.598936f, 0.655817f, 0.106015f, 0.693269f, 0.073462f, 0.686010f,
    0.083034f, 0.640197f, 0.136537f, 0.655816f, 0.662010f, 0.686010f, 0.653588f, 0.712098f,
    0.629457f, 0.693271f, 0.106015f, 0.693269f, 0.081883f, 0.712096f, 0.073462f, 0.686010f,
    0.615458f, 0.557583f, 0.703133f, 0.538140f, 0.706720f, 0.572934f, 0.633578f, 0.594596f,
    0.028756f, 0.572934f, 0.032336f, 0.538145f, 0.120011f, 0.557584f, 0.101895f, 0.594596f,
    0.615458f, 0.557583f, 0.633578f, 0.594596f, 0.559696f, 0.607669f, 0.512305f, 0.539237f,
    0.175778f, 0.607667f, 0.101895f, 0.594596f, 0.120011f, 0.557584f, 0.223171f, 0.539233f,
    0.393578f, 0.587505f, 0.512305f, 0.539237f, 0.559696f, 0.607669f, 0.493965f, 0.641024f,
    0.175778f, 0.607667f, 0.223171f, 0.539233f, 0.341892f, 0.587509f, 0.241508f, 0.641024f,
};

#define VERT_MARK 1

#define EDGE_ORIG 1
#define EDGE_MARK 2

#define FACE_MARK 1
#define FACE_NEW 2

void bmo_create_grid_exec(BMesh *bm, BMOperator *op)
{
  BMOpSlot *slot_verts_out = BMO_slot_get(op->slots_out, "verts.out");

  const float dia = BMO_slot_float_get(op->slots_in, "size");
  const uint xtot = max_ii(1, BMO_slot_int_get(op->slots_in, "x_segments"));
  const uint ytot = max_ii(1, BMO_slot_int_get(op->slots_in, "y_segments"));
  const float xtot_inv2 = 2.0f / (xtot);
  const float ytot_inv2 = 2.0f / (ytot);

  const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
  const bool calc_uvs = (cd_loop_uv_offset != -1) && BMO_slot_bool_get(op->slots_in, "calc_uvs");

  BMVert **varr;
  BMVert *vquad[4];

  float mat[4][4];
  float vec[3], tvec[3];

  uint x, y, i;

  BMO_slot_mat4_get(op->slots_in, "matrix", mat);

  BMO_slot_buffer_alloc(op, op->slots_out, "verts.out", (xtot + 1) * (ytot + 1));
  varr = (BMVert **)slot_verts_out->data.buf;

  i = 0;
  vec[2] = 0.0f;
  for (y = 0; y <= ytot; y++) {
    vec[1] = ((y * ytot_inv2) - 1.0f) * dia;
    for (x = 0; x <= xtot; x++) {
      vec[0] = ((x * xtot_inv2) - 1.0f) * dia;
      mul_v3_m4v3(tvec, mat, vec);
      varr[i] = BM_vert_create(bm, tvec, nullptr, BM_CREATE_NOP);
      BMO_vert_flag_enable(bm, varr[i], VERT_MARK);
      i++;
    }
  }

#define XY(_x, _y) ((_x) + ((_y) * (xtot + 1)))

  for (y = 1; y <= ytot; y++) {
    for (x = 1; x <= xtot; x++) {
      BMFace *f;

      vquad[0] = varr[XY(x - 1, y - 1)];
      vquad[1] = varr[XY(x, y - 1)];
      vquad[2] = varr[XY(x, y)];
      vquad[3] = varr[XY(x - 1, y)];

      f = BM_face_create_verts(bm, vquad, 4, nullptr, BM_CREATE_NOP, true);
      if (calc_uvs) {
        BMO_face_flag_enable(bm, f, FACE_MARK);
      }
    }
  }

#undef XY

  if (calc_uvs) {
    BM_mesh_calc_uvs_grid(bm, xtot, ytot, FACE_MARK, cd_loop_uv_offset);
  }
}

void BM_mesh_calc_uvs_grid(BMesh *bm,
                           const uint x_segments,
                           const uint y_segments,
                           const short oflag,
                           const int cd_loop_uv_offset)
{
  BMFace *f;
  BMLoop *l;
  BMIter iter, liter;

  const float dx = 1.0f / float(x_segments);
  const float dy = 1.0f / float(y_segments);
  const float dx_wrap = 1.0 - (dx / 2.0f);
  float x = 0.0f;
  float y = dy;

  int loop_index;

  BLI_assert(cd_loop_uv_offset != -1);

  BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
    if (!BMO_face_flag_test(bm, f, oflag)) {
      continue;
    }

    BM_ITER_ELEM_INDEX (l, &liter, f, BM_LOOPS_OF_FACE, loop_index) {
      float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);

      switch (loop_index) {
        case 0:
          y -= dy;
          break;
        case 1:
          x += dx;
          break;
        case 2:
          y += dy;
          break;
        case 3:
          x -= dx;
          break;
        default:
          break;
      }

      luv[0] = x;
      luv[1] = y;
    }

    x += dx;
    if (x >= dx_wrap) {
      x = 0.0f;
      y += dy;
    }
  }
}

void bmo_create_uvsphere_exec(BMesh *bm, BMOperator *op)
{
  const float rad = BMO_slot_float_get(op->slots_in, "radius");
  const int seg = BMO_slot_int_get(op->slots_in, "u_segments");
  const int tot = BMO_slot_int_get(op->slots_in, "v_segments");

  const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
  const bool calc_uvs = (cd_loop_uv_offset != -1) && BMO_slot_bool_get(op->slots_in, "calc_uvs");

  BMOperator bmop, prevop;
  BMVert *eve, *preveve;
  BMEdge *e;
  BMIter iter;
  const float axis[3] = {0, 0, 1};
  float vec[3], mat[4][4], cmat[3][3];
  int a;

  BMO_slot_mat4_get(op->slots_in, "matrix", mat);

  const float phid = float(M_PI) / tot;
  // const float phi = 0.25f * float(M_PI); /* UNUSED. */

  /* one segment first */
  for (a = 0; a <= tot; a++) {
    /* Going in this direction, then edge extruding, makes normals face outward */
    float sin_phi, cos_phi;
    sin_cos_from_fraction(a, 2 * tot, &sin_phi, &cos_phi);

    vec[0] = 0.0f;
    vec[1] = rad * sin_phi;
    vec[2] = rad * cos_phi;
    eve = BM_vert_create(bm, vec, nullptr, BM_CREATE_NOP);
    BMO_vert_flag_enable(bm, eve, VERT_MARK);

    if (a != 0) {
      e = BM_edge_create(bm, preveve, eve, nullptr, BM_CREATE_NOP);
      BMO_edge_flag_enable(bm, e, EDGE_ORIG);
    }

    preveve = eve;
  }

  /* extrude and rotate; negative phi to make normals face outward */
  axis_angle_to_mat3(cmat, axis, -(M_PI * 2) / seg);

  for (a = 0; a < seg; a++) {
    if (a) {
      BMO_op_initf(bm, &bmop, op->flag, "extrude_edge_only edges=%S", &prevop, "geom.out");
      BMO_op_exec(bm, &bmop);
      BMO_op_finish(bm, &prevop);
    }
    else {
      BMO_op_initf(bm, &bmop, op->flag, "extrude_edge_only edges=%fe", EDGE_ORIG);
      BMO_op_exec(bm, &bmop);
    }

    BMO_slot_buffer_flag_enable(bm, bmop.slots_out, "geom.out", BM_VERT, VERT_MARK);
    BMO_op_callf(bm, op->flag, "rotate cent=%v matrix=%m3 verts=%S", vec, cmat, &bmop, "geom.out");

    prevop = bmop;
  }

  if (a) {
    BMO_op_finish(bm, &bmop);
  }

  {
    float len, len2, vec2[3];

    len = 2 * rad * sinf(phid / 2.0f);

    /* Length of one segment in shortest parallel. */
    vec[0] = rad * sinf(phid);
    vec[1] = 0.0f;
    vec[2] = rad * cosf(phid);

    mul_v3_m3v3(vec2, cmat, vec);
    len2 = len_v3v3(vec, vec2);

    /* use shortest segment length divided by 3 as merge threshold */
    BMO_op_callf(
        bm, op->flag, "remove_doubles verts=%fv dist=%f", VERT_MARK, min_ff(len, len2) / 3.0f);
  }

  if (calc_uvs) {
    BMFace *f;
    BMLoop *l;
    BMIter fiter, liter;

    /* We cannot tag faces for UVs computing above,
     * so we have to do it now, based on all its vertices being tagged. */
    BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
      bool valid = true;

      BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
        if (!BMO_vert_flag_test(bm, l->v, VERT_MARK)) {
          valid = false;
          break;
        }
      }

      if (valid) {
        BMO_face_flag_enable(bm, f, FACE_MARK);
      }
    }

    BM_mesh_calc_uvs_sphere(bm, FACE_MARK, cd_loop_uv_offset);
  }

  /* Now apply the inverse matrix. */
  BM_ITER_MESH (eve, &iter, bm, BM_VERTS_OF_MESH) {
    if (BMO_vert_flag_test(bm, eve, VERT_MARK)) {
      mul_m4_v3(mat, eve->co);
    }
  }

  BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "verts.out", BM_VERT, VERT_MARK);
}

void bmo_create_icosphere_exec(BMesh *bm, BMOperator *op)
{
  const float rad = BMO_slot_float_get(op->slots_in, "radius");
  const float rad_div = rad / 200.0f;
  const int subdiv = BMO_slot_int_get(op->slots_in, "subdivisions");

  const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
  const bool calc_uvs = (cd_loop_uv_offset != -1) && BMO_slot_bool_get(op->slots_in, "calc_uvs");

  BMVert *eva[12];
  BMVert *v;
  BMIter liter;
  BMIter viter;
  BMLoop *l;
  float vec[3], mat[4][4] /* , phi, phid */;
  int a;

  BMO_slot_mat4_get(op->slots_in, "matrix", mat);

  // phid = 2.0f * float(M_PI) / subdiv; /* UNUSED. */
  // phi = 0.25f * float(M_PI);          /* UNUSED. */

  for (a = 0; a < 12; a++) {
    vec[0] = rad_div * icovert[a][0];
    vec[1] = rad_div * icovert[a][1];
    vec[2] = rad_div * icovert[a][2];
    eva[a] = BM_vert_create(bm, vec, nullptr, BM_CREATE_NOP);

    BMO_vert_flag_enable(bm, eva[a], VERT_MARK);
  }

  int uvi = 0;
  for (a = 0; a < 20; a++) {
    BMFace *f;
    BMVert *v1, *v2, *v3;

    v1 = eva[icoface[a][0]];
    v2 = eva[icoface[a][1]];
    v3 = eva[icoface[a][2]];

    f = BM_face_create_quad_tri(bm, v1, v2, v3, nullptr, nullptr, BM_CREATE_NOP);

    BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
      BMO_edge_flag_enable(bm, l->e, EDGE_MARK);
    }

    /* Set the UVs here, the iteration order of the faces is not guaranteed,
     * so it's best to set the UVs right after the face is created. */
    if (calc_uvs) {
      int loop_index;
      BM_ITER_ELEM_INDEX (l, &liter, f, BM_LOOPS_OF_FACE, loop_index) {
        float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
        luv[0] = icouvs[uvi][0];
        luv[1] = icouvs[uvi][1];
        uvi++;
      }
    }
  }

  if (subdiv > 1) {
    BMOperator bmop;

    BMO_op_initf(bm,
                 &bmop,
                 op->flag,
                 "subdivide_edges edges=%fe "
                 "smooth=%f "
                 "cuts=%i "
                 "use_grid_fill=%b use_sphere=%b",
                 EDGE_MARK,
                 rad,
                 (1 << (subdiv - 1)) - 1,
                 true,
                 true);

    BMO_op_exec(bm, &bmop);
    BMO_slot_buffer_flag_enable(bm, bmop.slots_out, "geom.out", BM_VERT, VERT_MARK);
    BMO_slot_buffer_flag_enable(bm, bmop.slots_out, "geom.out", BM_EDGE, EDGE_MARK);
    BMO_op_finish(bm, &bmop);
  }

  /* must transform after because of sphere subdivision */
  BM_ITER_MESH (v, &viter, bm, BM_VERTS_OF_MESH) {
    if (BMO_vert_flag_test(bm, v, VERT_MARK)) {
      mul_m4_v3(mat, v->co);
    }
  }

  BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "verts.out", BM_VERT, VERT_MARK);
}

static void bm_mesh_calc_uvs_sphere_face(BMFace *f, const int cd_loop_uv_offset)
{
  float *uvs[4];
  BMLoop *l;
  BMIter iter;
  float dx;
  int loop_index, loop_index_max_x;

  BLI_assert(f->len <= 4);

  /* If face has 3 vertices, it's a polar face, in which case we need to
   * compute a nearby to determine its latitude. */
  float avgx = 0.0f, avgy = 0.0f;
  BM_ITER_ELEM_INDEX (l, &iter, f, BM_LOOPS_OF_FACE, loop_index) {
    if (f->len == 3) {
      avgx += l->v->co[0];
      avgy += l->v->co[1];
    }
  }
  avgx /= 3.0f;
  avgy /= 3.0f;

  BM_ITER_ELEM_INDEX (l, &iter, f, BM_LOOPS_OF_FACE, loop_index) {
    float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
    float x = l->v->co[0];
    float y = l->v->co[1];
    float z = l->v->co[2];
    float len = len_v3(l->v->co);

    /* Use neighboring point to compute angle for poles. */
    float theta;
    if (f->len == 3 && fabsf(x) < 0.0001f && fabsf(y) < 0.0001f) {
      theta = atan2f(avgy, avgx);
    }
    else {
      theta = atan2f(y, x);
    }

    /* Shift borderline coordinates to the left. */
    if (fabsf(theta - float(M_PI)) < 0.0001f) {
      theta = -M_PI;
    }

    float phi = safe_acosf(z / len);
    luv[0] = 0.5f + theta / (float(M_PI) * 2);
    luv[1] = 1.0f - phi / float(M_PI);

    uvs[loop_index] = luv;
  }

  /* Fix awkwardly-wrapping UVs */
  loop_index_max_x = 0;
  for (loop_index = 1; loop_index < f->len; loop_index++) {
    if (uvs[loop_index][0] > uvs[loop_index_max_x][0]) {
      loop_index_max_x = loop_index;
    }
  }

  for (loop_index = 0; loop_index < f->len; loop_index++) {
    if (loop_index != loop_index_max_x) {
      dx = uvs[loop_index_max_x][0] - uvs[loop_index][0];
      if (dx > 0.5f) {
        uvs[loop_index][0] += 1.0f;
      }
    }
  }
}

void BM_mesh_calc_uvs_sphere(BMesh *bm, const short oflag, const int cd_loop_uv_offset)
{
  BMFace *f;
  BMIter iter;

  BLI_assert(cd_loop_uv_offset != -1); /* caller is responsible for giving us UVs */

  BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
    if (!BMO_face_flag_test(bm, f, oflag)) {
      continue;
    }

    bm_mesh_calc_uvs_sphere_face(f, cd_loop_uv_offset);
  }

  BMIter iter2;
  BMLoop *l;
  int loop_index;
  float minx = 1.0f;

  BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
    if (!BMO_face_flag_test(bm, f, oflag)) {
      continue;
    }
    BM_ITER_ELEM_INDEX (l, &iter2, f, BM_LOOPS_OF_FACE, loop_index) {
      float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
      if (luv[0] < minx) {
        minx = luv[0];
      }
    }
  }

  BM_ITER_MESH (f, &iter, bm, BM_FACES_OF_MESH) {
    if (!BMO_face_flag_test(bm, f, oflag)) {
      continue;
    }
    BM_ITER_ELEM_INDEX (l, &iter2, f, BM_LOOPS_OF_FACE, loop_index) {
      float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
      luv[0] -= minx;
    }
  }
}

void bmo_create_monkey_exec(BMesh *bm, BMOperator *op)
{
  BMVert **tv = static_cast<BMVert **>(MEM_mallocN(sizeof(*tv) * monkeynv * 2, "tv"));
  float mat[4][4];
  int i;

  BMO_slot_mat4_get(op->slots_in, "matrix", mat);

  const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
  const bool calc_uvs = (cd_loop_uv_offset != -1) && BMO_slot_bool_get(op->slots_in, "calc_uvs");

  for (i = 0; i < monkeynv; i++) {
    float v[3];

    /* rotate to face in the -Y axis */
    v[0] = (monkeyv[i][0] + 127) / 128.0;
    v[2] = monkeyv[i][1] / 128.0;
    v[1] = monkeyv[i][2] / -128.0;

    tv[i] = BM_vert_create(bm, v, nullptr, BM_CREATE_NOP);
    BMO_vert_flag_enable(bm, tv[i], VERT_MARK);

    if (fabsf(v[0] = -v[0]) < 0.001f) {
      tv[monkeynv + i] = tv[i];
    }
    else {
      BMVert *eve = BM_vert_create(bm, v, nullptr, BM_CREATE_NOP);
      mul_m4_v3(mat, eve->co);
      tv[monkeynv + i] = eve;
    }

    BMO_vert_flag_enable(bm, tv[monkeynv + i], VERT_MARK);

    mul_m4_v3(mat, tv[i]->co);
  }

  int uvi = 0;
  for (i = 0; i < monkeynf; i++) {
    BMFace *f_new_a = BM_face_create_quad_tri(
        bm,
        tv[monkeyf[i][0] + i - monkeyo],
        tv[monkeyf[i][1] + i - monkeyo],
        tv[monkeyf[i][2] + i - monkeyo],
        (monkeyf[i][3] != monkeyf[i][2]) ? tv[monkeyf[i][3] + i - monkeyo] : nullptr,
        nullptr,
        BM_CREATE_NOP);

    BMFace *f_new_b = BM_face_create_quad_tri(
        bm,
        tv[monkeynv + monkeyf[i][2] + i - monkeyo],
        tv[monkeynv + monkeyf[i][1] + i - monkeyo],
        tv[monkeynv + monkeyf[i][0] + i - monkeyo],
        (monkeyf[i][3] != monkeyf[i][2]) ? tv[monkeynv + monkeyf[i][3] + i - monkeyo] : nullptr,
        nullptr,
        BM_CREATE_NOP);

    /* Set the UVs here, the iteration order of the faces is not guaranteed,
     * so it's best to set the UVs right after the face is created. */
    if (calc_uvs) {
      BMLoop *l;
      BMIter liter;
      BM_ITER_ELEM (l, &liter, f_new_a, BM_LOOPS_OF_FACE) {
        float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
        luv[0] = monkeyuvs[uvi * 2 + 0];
        luv[1] = monkeyuvs[uvi * 2 + 1];
        uvi++;
      }
      BM_ITER_ELEM (l, &liter, f_new_b, BM_LOOPS_OF_FACE) {
        float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
        luv[0] = monkeyuvs[uvi * 2 + 0];
        luv[1] = monkeyuvs[uvi * 2 + 1];
        uvi++;
      }
    }
  }

  MEM_freeN(tv);

  BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "verts.out", BM_VERT, VERT_MARK);
}

void bmo_create_circle_exec(BMesh *bm, BMOperator *op)
{
  const float radius = BMO_slot_float_get(op->slots_in, "radius");
  const int segs = BMO_slot_int_get(op->slots_in, "segments");
  const bool cap_ends = BMO_slot_bool_get(op->slots_in, "cap_ends");
  const bool cap_tris = BMO_slot_bool_get(op->slots_in, "cap_tris");

  const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
  const bool calc_uvs = (cd_loop_uv_offset != -1) && BMO_slot_bool_get(op->slots_in, "calc_uvs");

  BMVert *v1, *lastv1 = nullptr, *cent1, *firstv1 = nullptr;
  float vec[3], mat[4][4];
  int a;

  if (!segs) {
    return;
  }

  BMO_slot_mat4_get(op->slots_in, "matrix", mat);

  if (cap_ends) {
    zero_v3(vec);
    mul_m4_v3(mat, vec);

    cent1 = BM_vert_create(bm, vec, nullptr, BM_CREATE_NOP);
    BMO_vert_flag_enable(bm, cent1, VERT_MARK);
  }

  for (a = 0; a < segs; a++) {
    /* Going this way ends up with normal(s) upward */
    sin_cos_from_fraction(a, segs, &vec[0], &vec[1]);
    vec[0] *= -radius;
    vec[1] *= radius;
    vec[2] = 0.0f;
    mul_m4_v3(mat, vec);
    v1 = BM_vert_create(bm, vec, nullptr, BM_CREATE_NOP);

    BMO_vert_flag_enable(bm, v1, VERT_MARK);

    if (lastv1) {
      BM_edge_create(bm, v1, lastv1, nullptr, BM_CREATE_NOP);
    }

    if (a && cap_ends) {
      BMFace *f;

      f = BM_face_create_quad_tri(bm, cent1, lastv1, v1, nullptr, nullptr, BM_CREATE_NOP);
      BMO_face_flag_enable(bm, f, FACE_NEW);
    }

    if (!firstv1) {
      firstv1 = v1;
    }

    lastv1 = v1;
  }

  if (!a) {
    return;
  }

  BM_edge_create(bm, firstv1, lastv1, nullptr, eBMCreateFlag(0));

  if (cap_ends) {
    BMFace *f;

    f = BM_face_create_quad_tri(bm, cent1, v1, firstv1, nullptr, nullptr, BM_CREATE_NOP);
    BMO_face_flag_enable(bm, f, FACE_NEW);

    if (calc_uvs) {
      BM_mesh_calc_uvs_circle(bm, mat, radius, FACE_NEW, cd_loop_uv_offset);
    }
  }

  if (!cap_tris) {
    BMO_op_callf(bm, op->flag, "dissolve_faces faces=%ff", FACE_NEW);
  }

  BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "verts.out", BM_VERT, VERT_MARK);
}

void BM_mesh_calc_uvs_circle(
    BMesh *bm, float mat[4][4], const float radius, const short oflag, const int cd_loop_uv_offset)
{
  BMFace *f;
  BMLoop *l;
  BMIter fiter, liter;

  const float uv_scale = 0.5f / radius;
  const float uv_center = 0.5f;

  float inv_mat[4][4];

  BLI_assert(cd_loop_uv_offset != -1); /* caller must ensure we have UVs already */

  invert_m4_m4(inv_mat, mat);

  BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
    if (!BMO_face_flag_test(bm, f, oflag)) {
      continue;
    }

    BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
      float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);

      float uv_vco[3];
      copy_v3_v3(uv_vco, l->v->co);
      /* transform back into the unit circle flat on the Z-axis */
      mul_m4_v3(inv_mat, uv_vco);

      /* then just take those coords for UVs */
      luv[0] = uv_center + uv_scale * uv_vco[0];
      luv[1] = uv_center + uv_scale * uv_vco[1];
    }
  }
}

void bmo_create_cone_exec(BMesh *bm, BMOperator *op)
{
  BMVert *v1, *v2, *lastv1 = nullptr, *lastv2 = nullptr, *cent1, *cent2, *firstv1, *firstv2;
  BMFace *f;
  float vec[3], mat[4][4];
  const float rad1 = BMO_slot_float_get(op->slots_in, "radius1");
  const float rad2 = BMO_slot_float_get(op->slots_in, "radius2");
  const float depth_half = 0.5f * BMO_slot_float_get(op->slots_in, "depth");
  int segs = BMO_slot_int_get(op->slots_in, "segments");
  const bool cap_ends = BMO_slot_bool_get(op->slots_in, "cap_ends");
  const bool cap_tris = BMO_slot_bool_get(op->slots_in, "cap_tris");

  const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
  const bool calc_uvs = (cd_loop_uv_offset != -1) && BMO_slot_bool_get(op->slots_in, "calc_uvs");

  if (!segs) {
    return;
  }

  BMO_slot_mat4_get(op->slots_in, "matrix", mat);

  if (cap_ends) {
    vec[0] = vec[1] = 0.0f;
    vec[2] = -depth_half;
    mul_m4_v3(mat, vec);

    cent1 = BM_vert_create(bm, vec, nullptr, BM_CREATE_NOP);

    vec[0] = vec[1] = 0.0f;
    vec[2] = depth_half;
    mul_m4_v3(mat, vec);

    cent2 = BM_vert_create(bm, vec, nullptr, BM_CREATE_NOP);

    BMO_vert_flag_enable(bm, cent1, VERT_MARK);
    BMO_vert_flag_enable(bm, cent2, VERT_MARK);
  }

  const int side_faces_len = segs - 1;
  BMFace **side_faces = static_cast<BMFace **>(
      MEM_mallocN(sizeof(*side_faces) * side_faces_len, __func__));

  for (int i = 0; i < segs; i++) {
    /* Calculate with higher precision, see: #87779. */
    float sin_phi, cos_phi;
    sin_cos_from_fraction(i, segs, &sin_phi, &cos_phi);

    vec[0] = rad1 * sin_phi;
    vec[1] = rad1 * cos_phi;
    vec[2] = -depth_half;
    mul_m4_v3(mat, vec);
    v1 = BM_vert_create(bm, vec, nullptr, BM_CREATE_NOP);

    vec[0] = rad2 * sin_phi;
    vec[1] = rad2 * cos_phi;
    vec[2] = depth_half;
    mul_m4_v3(mat, vec);
    v2 = BM_vert_create(bm, vec, nullptr, BM_CREATE_NOP);

    BMO_vert_flag_enable(bm, v1, VERT_MARK);
    BMO_vert_flag_enable(bm, v2, VERT_MARK);

    if (i) {
      if (cap_ends) {
        f = BM_face_create_quad_tri(bm, cent1, lastv1, v1, nullptr, nullptr, BM_CREATE_NOP);
        if (calc_uvs) {
          BMO_face_flag_enable(bm, f, FACE_MARK);
        }
        BMO_face_flag_enable(bm, f, FACE_NEW);

        f = BM_face_create_quad_tri(bm, cent2, v2, lastv2, nullptr, nullptr, BM_CREATE_NOP);
        if (calc_uvs) {
          BMO_face_flag_enable(bm, f, FACE_MARK);
        }
        BMO_face_flag_enable(bm, f, FACE_NEW);
      }

      f = BM_face_create_quad_tri(bm, lastv1, lastv2, v2, v1, nullptr, BM_CREATE_NOP);
      if (calc_uvs) {
        BMO_face_flag_enable(bm, f, FACE_MARK);
      }
      side_faces[i - 1] = f;
    }
    else {
      firstv1 = v1;
      firstv2 = v2;
    }

    lastv1 = v1;
    lastv2 = v2;
  }

  if (cap_ends) {
    f = BM_face_create_quad_tri(bm, cent1, v1, firstv1, nullptr, nullptr, BM_CREATE_NOP);
    if (calc_uvs) {
      BMO_face_flag_enable(bm, f, FACE_MARK);
    }
    BMO_face_flag_enable(bm, f, FACE_NEW);

    f = BM_face_create_quad_tri(bm, cent2, firstv2, v2, nullptr, nullptr, BM_CREATE_NOP);
    if (calc_uvs) {
      BMO_face_flag_enable(bm, f, FACE_MARK);
    }
    BMO_face_flag_enable(bm, f, FACE_NEW);
  }

  f = BM_face_create_quad_tri(bm, v1, v2, firstv2, firstv1, nullptr, BM_CREATE_NOP);
  if (calc_uvs) {
    BMO_face_flag_enable(bm, f, FACE_MARK);
  }

  if (calc_uvs) {
    BM_mesh_calc_uvs_cone(bm, mat, rad2, rad1, segs, cap_ends, FACE_MARK, cd_loop_uv_offset);
  }

  /* Collapse vertices at the first end. */
  if (rad1 == 0.0f) {
    if (cap_ends) {
      BM_vert_kill(bm, cent1);
    }
    for (int i = 0; i < side_faces_len; i++) {
      f = side_faces[i];
      BMLoop *l = BM_FACE_FIRST_LOOP(f);
      BM_edge_collapse(bm, l->prev->e, l->prev->v, true, true);
    }
  }

  /* Collapse vertices at the second end. */
  if (rad2 == 0.0f) {
    if (cap_ends) {
      BM_vert_kill(bm, cent2);
    }
    for (int i = 0; i < side_faces_len; i++) {
      f = side_faces[i];
      BMLoop *l = BM_FACE_FIRST_LOOP(f);
      BM_edge_collapse(bm, l->next->e, l->next->v, true, true);
    }
  }

  if (!cap_tris) {
    BMO_op_callf(bm, op->flag, "dissolve_faces faces=%ff", FACE_NEW);
  }

  if (side_faces != nullptr) {
    MEM_freeN(side_faces);
  }

  BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "verts.out", BM_VERT, VERT_MARK);
}

void BM_mesh_calc_uvs_cone(BMesh *bm,
                           float mat[4][4],
                           const float radius_top,
                           const float radius_bottom,
                           const int segments,
                           const bool cap_ends,
                           const short oflag,
                           const int cd_loop_uv_offset)
{
  BMFace *f;
  BMLoop *l;
  BMIter fiter, liter;

  const float uv_width = 1.0f / float(segments);
  const float uv_height = cap_ends ? 0.5f : 1.0f;

  /* Note that all this allows us to handle all cases
   * (real cone, truncated cone, with or without ends capped)
   * with a single common code. */
  const float uv_center_y = cap_ends ? 0.25f : 0.5f;
  const float uv_center_x_top = cap_ends ? 0.25f : 0.5f;
  const float uv_center_x_bottom = cap_ends ? 0.75f : 0.5f;
  const float uv_radius = cap_ends ? 0.24f : 0.5f;

  /* Using the opposite's end uv_scale as fallback allows us to handle 'real cone' case. */
  const float uv_scale_top = (radius_top != 0.0f) ?
                                 (uv_radius / radius_top) :
                                 ((radius_bottom != 0.0f) ? (uv_radius / radius_bottom) :
                                                            uv_radius);
  const float uv_scale_bottom = (radius_bottom != 0.0f) ? (uv_radius / radius_bottom) :
                                                          uv_scale_top;

  float local_up[3] = {0.0f, 0.0f, 1.0f};

  float x, y;
  float inv_mat[4][4];
  int loop_index;

  /* Transform the up-vector like we did the cone itself, without location. */
  mul_mat3_m4_v3(mat, local_up);
  /* Remove global scaling. */
  normalize_v3(local_up);

  invert_m4_m4(inv_mat, mat);

  BLI_assert(cd_loop_uv_offset != -1); /* caller is responsible for ensuring the mesh has UVs */

  x = 1.0f;
  y = 1.0f - uv_height;

  BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
    if (!BMO_face_flag_test(bm, f, oflag)) {
      continue;
    }

    if (f->len == 4 && radius_top && radius_bottom) {
      /* side face - so unwrap it in a rectangle */
      BM_ITER_ELEM_INDEX (l, &liter, f, BM_LOOPS_OF_FACE, loop_index) {
        float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);

        switch (loop_index) {
          case 0:
            /* Continue in the last position */
            break;
          case 1:
            y += uv_height;
            break;
          case 2:
            x -= uv_width;
            break;
          case 3:
            y -= uv_height;
            break;
          default:
            break;
        }

        luv[0] = x;
        luv[1] = y;
      }
    }
    else {
      /* Top or bottom face - so unwrap it by transforming
       * back to a circle and using the X/Y coords. */
      BM_face_normal_update(f);

      BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
        float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);
        float uv_vco[3];

        mul_v3_m4v3(uv_vco, inv_mat, l->v->co);

        if (dot_v3v3(f->no, local_up) > 0.0f) { /* if this is a top face of the cone */
          luv[0] = uv_center_x_top + uv_vco[0] * uv_scale_top;
          luv[1] = uv_center_y + uv_vco[1] * uv_scale_top;
        }
        else {
          luv[0] = uv_center_x_bottom + uv_vco[0] * uv_scale_bottom;
          luv[1] = uv_center_y + uv_vco[1] * uv_scale_bottom;
        }
      }
    }
  }
}

void bmo_create_cube_exec(BMesh *bm, BMOperator *op)
{
  BMVert *verts[8];
  float mat[4][4];
  float off = BMO_slot_float_get(op->slots_in, "size") / 2.0f;

  const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);
  const bool calc_uvs = (cd_loop_uv_offset != -1) && BMO_slot_bool_get(op->slots_in, "calc_uvs");

  /* rotation order set to match 'BM_mesh_calc_uvs_cube' */
  const char faces[6][4] = {
      {0, 1, 3, 2},
      {2, 3, 7, 6},
      {6, 7, 5, 4},
      {4, 5, 1, 0},
      {2, 6, 4, 0},
      {7, 3, 1, 5},
  };

  BMO_slot_mat4_get(op->slots_in, "matrix", mat);

  if (!off) {
    off = 0.5f;
  }
  int i = 0;

  for (int x = -1; x < 2; x += 2) {
    for (int y = -1; y < 2; y += 2) {
      for (int z = -1; z < 2; z += 2) {
        float vec[3] = {float(x) * off, float(y) * off, float(z) * off};
        mul_m4_v3(mat, vec);
        verts[i] = BM_vert_create(bm, vec, nullptr, BM_CREATE_NOP);
        BMO_vert_flag_enable(bm, verts[i], VERT_MARK);
        i++;
      }
    }
  }

  for (i = 0; i < ARRAY_SIZE(faces); i++) {
    BMFace *f;
    BMVert *quad[4] = {
        verts[faces[i][0]],
        verts[faces[i][1]],
        verts[faces[i][2]],
        verts[faces[i][3]],
    };

    f = BM_face_create_verts(bm, quad, 4, nullptr, BM_CREATE_NOP, true);
    if (calc_uvs) {
      BMO_face_flag_enable(bm, f, FACE_MARK);
    }
  }

  if (calc_uvs) {
    BM_mesh_calc_uvs_cube(bm, FACE_MARK);
  }

  BMO_slot_buffer_from_enabled_flag(bm, op, op->slots_out, "verts.out", BM_VERT, VERT_MARK);
}

void BM_mesh_calc_uvs_cube(BMesh *bm, const short oflag)
{
  BMFace *f;
  BMLoop *l;
  BMIter fiter, liter;
  const float width = 0.25f;

  const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_PROP_FLOAT2);

  float x = 0.375f;
  float y = 0.0f;

  int loop_index;

  BLI_assert(cd_loop_uv_offset != -1); /* the caller can ensure that we have UVs */

  BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
    if (!BMO_face_flag_test(bm, f, oflag)) {
      continue;
    }

    BM_ITER_ELEM_INDEX (l, &liter, f, BM_LOOPS_OF_FACE, loop_index) {
      float *luv = BM_ELEM_CD_GET_FLOAT_P(l, cd_loop_uv_offset);

      luv[0] = x;
      luv[1] = y;

      switch (loop_index) {
        case 0:
          x += width;
          break;
        case 1:
          y += width;
          break;
        case 2:
          x -= width;
          break;
        case 3:
          y -= width;
          break;
        default:
          break;
      }
    }

    if (y >= 0.75f && x > 0.125f) {
      x = 0.125f;
      y = 0.5f;
    }
    else if (x <= 0.125f) {
      x = 0.625f;
      y = 0.5f;
    }
    else {
      y += 0.25f;
    }
  }
}
